Encuentre el conjunto óptimo de pesos para agregar a un determinado conjunto de pesos

8

En este desafío, recibirá una lista de pesos separados por comas como entrada, como

1,3,4,7,8,11

Y debe generar la menor cantidad de pesos que pueda agregar a ese conjunto. Por ejemplo, la salida para este conjunto sería

1,3,7

Porque podrías representar todos esos pesos con solo esos tres:

1     = 1
3     = 3
1+3   = 4
7     = 7
1+7   = 8
1+3+7 = 11

Puede haber más de una solución. Por ejemplo, su solución para la entrada 1,2podría ser 1,1o 1,2. Siempre que encuentre la cantidad mínima de pesos que puede representar el conjunto de entrada, es una solución válida.

Las pesas no se pueden usar más de una vez. Si necesita usar uno dos veces, debe generarlo dos veces. Por ejemplo, 2,3no es una solución válida 2,3,5,7porque no puede usar el 2doble para 2+2+3=7.

Se garantiza que la entrada no tenga números duplicados.

Esto es entonces el código más corto por conteo de caracteres gana.

El acceso a la red está prohibido (por lo que ninguna de sus wgetsoluciones "inteligentes" @JohannesKuhn tose tos );)

Casos más simples:

1,5,6,9,10,14,15               => 1,5,9
7,14,15,21,22,29               => 7,14,15
4,5,6,7,9,10,11,12,13,15,16,18 => 4,5,6,7
2,3,5,7                        => 2,2,3 or 2,3,7

Y algunos más complicados:

10,16,19,23,26,27,30,37,41,43,44,46,50,53,57,60,61,64,68,71,77,80,84,87
  => 3,7,16,27,34
20,30,36,50,56,63,66,73,79,86
  => 7,13,23,43
27,35,44,46,51,53,55,60,63,64,68,69,72,77,79,81,86,88,90,95,97,105,106,114,123,132
  => 9,18,26,37,42
Pomo de la puerta
fuente
muy similar a codegolf.stackexchange.com/questions/12399/…
John Dvorak el
@ Jan, una diferencia significativa es que el desafío que usted cita requiere un conjunto, mientras que este permite duplicados (por ejemplo, 7,7,7,8arriba), lo que aumenta la complejidad muchas veces.
Cary Swoveland el
¿Podemos suponer que los pesos de entrada son únicos (por lo que no tenemos que eliminar dups, así de simple)? Además, puede considerar exigir que las soluciones puedan resolver un caso de prueba dado; de lo contrario, la solución más corta puede ser un enumerador de fuerza bruta que solo puede lidiar con pequeños problemas (por ejemplo, si hay npesos de entrada y mes el más grande, enumere todas las subsecuencias de (1..m)y para cada subsecuencia, enumere cada combinación de entre 1 e ninstancias de cada elemento de la secuencia.)
Cary Swoveland el
@CarySwoveland Editado para la parte "única". Ya tengo casos de prueba.
Pomo de la puerta
¿Cómo puede {7,7,7,8} ser una solución? 8 no está en el conjunto de entrada.
DavidC

Respuestas:

3

Mathematica 80 75

Actualización: vea al final una actualización sobre la última prueba desafiante de Doorknob, agregada el 5 de noviembre


Esto pasa todo menos la última prueba. Sin embargo, no intenta usar un dígito más de una vez. Y solo busca en soluciones que son subconjuntos del conjunto de datos más grande.

La función genera todos los subconjuntos del conjunto de datos de entrada y luego prueba qué subconjuntos se pueden usar para construir el conjunto completo. Después de encontrar los subconjuntos viables, elige los conjuntos más pequeños.

s=Subsets
f@i_:=GatherBy[Select[s@i,Complement[i, Total /@ s@#]=={}&],Length]〚1〛

Pruebas

f[{1, 3, 4, 7, 8, 11}]

{{1, 3, 7}}


f[{1, 5, 6, 9, 10, 14, 15}]

{{1, 5, 9}}


f[{7, 14, 15, 21, 22, 29}]

{{7, 14, 15}}


f[{4, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 18}]

{{4, 5, 6, 7}}


f[{2, 3, 5, 7}]

{{2, 3, 5}, {2, 3, 7}}


Actualizar

A continuación, proporcionaré un análisis inicial que puede ayudar a comenzar a encontrar una solución.

Los datos:

data = {10, 16, 19, 23, 26, 27, 30, 37, 41, 43, 44, 46, 50, 53, 57, 60, 61, 64, 68, 71, 77, 80, 84, 87};

A diferencia del enfoque anterior, queremos considerar, en el conjunto de soluciones, los números que NO aparecen en el conjunto de datos.

El enfoque hace uso de diferencias absolutas entre pares de números en el conjunto de datos.

g[d_] := DeleteCases[Reverse@SortBy[Tally[Union[Sort /@ Tuples[d, {2}]] /. {a_, b_} :> Abs[a - b]], Last], {0, _}] 

Veamos la cantidad de veces que aparece cada diferencia; solo tomaremos los primeros 8 casos, comenzando por la diferencia más común].

g[data][[1;;8]]

{{7, 14}, {27, 13}, {34, 12}, {3, 11}, {20, 10}, {16, 10}, {4, 10}, {11, 9}}

14 pares diferían en 7; 13 pares diferían en 27, y así sucesivamente.

Ahora, probemos los subconjuntos que comienzan con {diferencia1}, {diferencia1, diferencia2}, y así sucesivamente, hasta que podamos tener en cuenta todos los elementos originales del conjunto de datos.

h revela aquellos números del conjunto original que no pueden construirse componiendo sumas del subconjunto.

h[t_] := Complement[data, Total /@ Subsets@t]

Para el quinto intento, todavía hay 10 elementos que no se pueden formar a partir de {7, 27, 34, 3, 20}:

h[{7, 27, 34, 3, 20}]

{16, 19, 26, 43, 46, 53, 60, 77, 80, 87}

Pero en el próximo intento, se tienen en cuenta todos los números del conjunto de datos:

h[{7, 27, 34, 3, 20, 16}]

{}

Esto todavía no es tan económico como {3,7,16,27,34}, pero está cerca.


Todavía hay algunas cosas adicionales a tener en cuenta.

  1. Si 1 está en el conjunto de datos, será necesario en el conjunto de soluciones.
  2. Puede haber algunos "solitarios" en el conjunto original que no se pueden componer de las diferencias más comunes. Deberían incluirse aparte de las pruebas de diferencia.

Estos son más problemas de los que puedo manejar en este momento. Pero espero que arroje algo de luz sobre este desafío tan interesante.

DavidC
fuente
hmm ... Actualmente la elaboración de caso de prueba que requiere duplicados: P
Pomo
Dejaré mi solución publicada por ahora y veré si puedo agregar una condición para probar duplicados.
DavidC
3
Si existe una solución en la que wse repite un peso , entonces la misma solución con uno de los ws cambiados 2 * wtambién funciona, ya que puede usar 2 * wtodos los lugares que usaba w + wantes. Esto puede repetirse hasta que la solución no tenga repeticiones. Por lo tanto, no necesita intentar usar repeticiones.
caja de cartón
Realmente no necesitas el paréntesis. Obtener el s=Subsets;cabo de la función
Dr. Belisario
Correcto sobre los paréntesis.
DavidC
0

Rubí 289

Esta es una enumeración directa, por lo que obtendrá soluciones mínimas, pero puede llevar años, posiblemente años luz, resolver algunos problemas. Todos los "casos más simples" se resuelven en unos segundos como máximo (aunque obtuve 7,8,14 y 1,2,4 para los casos 3 y 5, respectivamente). El complicado # 2 se resolvió en aproximadamente 3 horas, pero los otros dos son demasiado grandes para enumerarlos, al menos por la forma en que lo hice.

Una matriz de tamaño nque genera la matriz dada sumando subconjuntos de sus elementos es de tamaño mínimo si se puede demostrar que no hay una matriz de tamaño < nque lo haga. No puedo ver otra manera de demostrar la optimización, así que comienzo la enumeración con subconjuntos de tamaño m, donde mse conoce un límite inferior, y luego aumento el tamaño a m+1después de haber enumerado subconjuntos de tamaño my se muestra que ninguno de esos "abarcan" el dado matriz, y así sucesivamente, hasta que encuentre un óptimo. Por supuesto, si he enumerado todos los subconjuntos hasta el tamaño n, podría usar una heurística para el tamaño n + 1, de modo que si encontrara una matriz de ese tamaño, sabría que es óptima. ¿Alguien puede sugerir una forma alternativa de demostrar que una solución es óptima en el caso general?

He incluido algunas verificaciones opcionales para eliminar algunas combinaciones desde el principio. Eliminar esos cheques ahorraría 87 caracteres. Son los siguientes ( aes la matriz dada):

  • una matriz de tamaño npuede generar en la mayoría de 2^n-1los números positivos distintos; por lo tanto, 2^n-1 >= a.sizeo n >= log2(a.size).ceil(el "límite inferior" al que me referí anteriormente).
  • Un conjunto generador bde tamaño candidato npuede descartarse si:
    • b.min > a.min
    • sum of elements of b < a.max o
    • b.max < v, donde v = a.max.to_f/n+(n-1).to_f/2.ceil( to_fsiendo conversión a flotante).

El último de estos, que se verifica primero, implementa

sum of elements of b <= sum(b.max-n+1..b.max) < a.max

La nota ves constante para todas las matrices de generadores de tamaño n.

También he utilizado la observación muy útil de @ cartón_box de que no hay necesidad de considerar duplicados en la matriz generadora.

En mi código

(1..a.max).to_a.combination(n) 

genera todas las combinaciones de los números del 1 al a.max, tomadas na la vez (donde a.max = a.last = a[-1]). Para cada combinación b:

(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}

llena un hash hcon todos los números que son sumas sobre subconjuntos no vacíos de b. Las claves hash son esos números; Los valores son arbitrarios. (Elegí establecer el último en cero).

a.all?{|e|h[e]}}

comprueba si cada elemento de la matriz dada aes una clave en el hash ( h[e] != nilo simplemente h[e]).

Suponer

n = 3 and b=[2,5,7].

Luego iteramos sobre el rango:

(1...2**8) = (1...8) # 1,2,..,7

La representación binaria de cada número en este rango se utiliza para apuñalar los elementos de bsuma. Para j = 3( jsiendo el índice de rango),

3.to_s(2)                  # =>  "11"
"11".rjust(3,?0)           # => "011"
"011".split('')            # => ["0","1","1"]
[2,5,7].zip(["1","0","1"]) # => [[2,"0"],[5,"1"],[7,"1"]]
[[2,"0"],[5,"1"],[7,"1"]].reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0 # => t = 0+5+7 = 12 

El código:

x=a[-1]
n=Math.log2(a.size).ceil
loop do
v=(1.0*x/n+(n-1)/2.0).ceil
(1..x).to_a.combination(n).each{|b|
next if b[-1]<v||b[0]>a[0]||b.reduce(&:+)<x
h={}
(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}
(p b;exit)if a.all?{|e|h[e]}}
n+=1
end
Cary Swoveland
fuente