Finden Sie den optimalen Satz von Gewichten, um ihn einem bestimmten Satz von Gewichten hinzuzufügen

8

Bei dieser Herausforderung erhalten Sie eine durch Kommas getrennte Liste von Gewichten als Eingabe, z

1,3,4,7,8,11

Und Sie müssen die kleinste Menge an Gewichten ausgeben, die zu diesem Satz hinzugefügt werden können. Zum Beispiel wäre die Ausgabe für diesen Satz

1,3,7

Weil Sie all diese Gewichte mit nur diesen drei darstellen könnten:

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

Es kann mehr als eine Lösung geben. Zum Beispiel Ihre Lösung für die Eingabe 1,2könnte sein , 1,1oder 1,2. Solange die Mindestanzahl an Gewichten gefunden wird, die den Eingabesatz darstellen können, ist dies eine gültige Lösung.

Gewichte dürfen nicht mehr als einmal verwendet werden. Wenn Sie eine zweimal verwenden müssen, müssen Sie sie zweimal ausgeben. Dies ist beispielsweise 2,3keine gültige Lösung für, 2,3,5,7da Sie das nicht 2zweimal verwenden können 2+2+3=7.

Die Eingabe enthält garantiert keine doppelten Nummern.

Dies ist so dass der kürzeste Code nach Zeichenanzahl gewinnt.

Der Netzwerkzugriff ist verboten (also keine Ihrer "cleveren" wgetLösungen @JohannesKuhn Husten Husten );)

Einfachste Fälle:

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

Und einige schwierigere:

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
Türknauf
quelle
sehr ähnlich zu codegolf.stackexchange.com/questions/12399/…
John Dvorak
@Jan, ein wesentlicher Unterschied besteht darin, dass die von Ihnen zitierte Herausforderung eine Menge erforderte, während diese Duplikate (z. B. 7,7,7,8oben) zulässt , was die Komplexität um ein Vielfaches erhöht.
Cary Swoveland
Können wir davon ausgehen, dass die Eingabegewichte eindeutig sind (so dass wir keine Dups entfernen müssen, so einfach das wäre)? Möglicherweise müssen Sie auch verlangen, dass Lösungen einen bestimmten Testfall lösen können. Andernfalls kann die kürzeste Lösung ein Brute-Force-Enumerator sein, der nur winzige Probleme lösen kann (z. B. wenn es nEingangsgewichte gibt und mdas größte ist, alle Teilsequenzen von aufzählen (1..m)und für jede Teilsequenz jede Kombination von 1 und nInstanzen von jedem aufzählen Element der Sequenz.)
Cary Swoveland
@CarySwoveland Für den "einzigartigen" Teil bearbeitet. Ich habe bereits Testfälle.
Türknauf
Wie kann {7,7,7,8} eine Lösung sein? 8 ist nicht im Eingangssatz enthalten.
DavidC

Antworten:

3

Mathematica 80 75

Update: Unten finden Sie ein Update zu Doorknobs herausforderndem letzten Test, der am 5. November hinzugefügt wurde


Dies besteht alle bis auf den letzten Test. Es wird jedoch nicht versucht, eine Ziffer mehr als einmal zu verwenden. Und es wird nur nach Lösungen gesucht, die Teilmengen des größeren Datensatzes sind.

Die Funktion generiert alle Teilmengen des Eingabedatensatzes und testet dann, welche Teilmengen zum Erstellen des vollständigen Satzes verwendet werden können. Nachdem die realisierbaren Teilmengen gefunden wurden, werden die kleinsten Mengen ausgewählt.

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

Tests

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}}


Aktualisieren

Im Folgenden werde ich eine erste Analyse bereitstellen, die Ihnen dabei helfen kann, eine Lösung zu finden.

Die Daten:

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};

Anders als beim früheren Ansatz möchten wir im Lösungssatz Zahlen berücksichtigen, die NICHT im Datensatz enthalten sind.

Der Ansatz nutzt absolute Unterschiede zwischen Zahlenpaaren im Datensatz.

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

Schauen wir uns an, wie oft jeder Unterschied auftritt. Wir werden nur die ersten 8 Fälle behandeln, beginnend mit dem häufigsten Unterschied.

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

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

14 Paare unterschieden sich um 7; 13 Paare unterschieden sich um 27 und so weiter.

Testen wir nun Teilmengen, die mit {Differenz1}, {Differenz1, Differenz2} usw. beginnen, bis wir hoffentlich alle ursprünglichen Elemente im Datensatz berücksichtigen können.

h zeigt die Zahlen aus der ursprünglichen Menge an, die nicht durch Zusammensetzen von Summen aus der Teilmenge konstruiert werden können.

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

Beim fünften Versuch gibt es noch 10 Elemente, die nicht aus {7, 27, 34, 3, 20} gebildet werden können:

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

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

Beim nächsten Versuch werden jedoch alle Nummern des Datensatzes berücksichtigt:

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

{}

Dies ist immer noch nicht so wirtschaftlich wie {3,7,16,27,34}, aber es ist nah.


Es sind noch einige zusätzliche Dinge zu berücksichtigen.

  1. Wenn 1 im Datensatz enthalten ist, wird es im Lösungssatz benötigt.
  2. Es kann durchaus einige "Einzelgänger" im Originalset geben, die sich nicht aus den häufigsten Unterschieden zusammensetzen lassen. Diese müssten abgesehen von den Differenztests einbezogen werden.

Dies sind mehr Probleme, als ich im Moment behandeln kann. Aber ich hoffe, es wirft ein Licht auf diese sehr interessante Herausforderung.

DavidC
quelle
hmm ... entwickelt derzeit einen Testfall, der Duplikate erfordert: P
Türknauf
Ich werde meine Lösung vorerst veröffentlicht lassen und prüfen, ob ich eine Bedingung zum Testen von Duplikaten hinzufügen kann.
DavidC
3
Wenn es eine Lösung gibt, bei der ein Gewicht wwiederholt wird, funktioniert dieselbe Lösung, bei der eines der ws geändert wurde, 2 * wauch, da Sie die Lösung 2 * wüberall verwenden können, wo Sie sie w + wzuvor verwendet haben. Dies kann wiederholt werden, bis die Lösung keine Wiederholungen mehr aufweist. Daher müssen Sie nicht versuchen, Wiederholungen zu verwenden.
cardboard_box
Sie brauchen die Klammer nicht wirklich. Holen Sie s=Subsets;sich aus der Funktion
Dr. belisarius
Richtig zu den Klammern.
DavidC
0

Ruby 289

Dies ist eine direkte Aufzählung, sodass nur minimale Lösungen erzielt werden. Es kann jedoch Jahre - möglicherweise Lichtjahre - dauern, bis einige Probleme gelöst sind. Alle "einfachsten Fälle" lösen sich in höchstens wenigen Sekunden (obwohl ich für den 3. bzw. 5. Fall 7,8,14 und 1,2,4 erhalten habe). Tricky # 2 wurde in ungefähr 3 Stunden gelöst, aber die anderen beiden sind einfach zu groß für die Aufzählung, zumindest für die Art und Weise, wie ich es gemacht habe.

Ein Array mit einer Größe n, die das angegebene Array durch Summieren von Teilmengen seiner Elemente generiert, hat eine minimale Größe, wenn gezeigt werden kann, dass es kein Array mit einer Größe gibt < n, die dies tut. Ich kann keinen anderen Weg sehen, um die Optimalität zu beweisen, also beginne ich die Aufzählung mit Teilmengen der Größe m, wobei mes sich um eine bekannte Untergrenze handelt, und erhöhe dann die Größe auf, m+1nachdem ich Teilmengen der Größe aufgezählt mund gezeigt habe, dass sie keine dieser "Spannen" der angegebenen umfassen Array und so weiter, bis ich ein Optimum finde. Wenn ich alle Teilmengen bis zur Größe n aufgelistet habe, könnte ich natürlich eine Heuristik für die Größe n + 1 verwenden. Wenn ich also ein übergreifendes Array dieser Größe finden würde, würde ich wissen, dass es optimal ist. Kann jemand einen alternativen Weg vorschlagen, um zu beweisen, dass eine Lösung im allgemeinen Fall optimal ist?

Ich habe einige optionale Überprüfungen hinzugefügt, um einige Kombinationen frühzeitig zu eliminieren. Das Entfernen dieser Überprüfungen würde 87 Zeichen sparen. Sie sind wie folgt ( aist das gegebene Array):

  • Ein Array von Größen nkann höchstens 2^n-1unterschiedliche positive Zahlen erzeugen . daher 2^n-1 >= a.sizeoder n >= log2(a.size).ceil(die "Untergrenze", auf die ich oben Bezug genommen habe).
  • Ein Kandidat, der ein Array mit beiner Größe generiert, nkann ausgeschlossen werden, wenn:
    • b.min > a.min
    • sum of elements of b < a.max oder
    • b.max < v, wo v = a.max.to_f/n+(n-1).to_f/2.ceil( to_fUmwandlung in Float).

Die letzte davon, die zuerst überprüft wird, implementiert

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

Hinweis vist für alle Generator-Arrays der Größe konstant n.

Ich habe auch die sehr hilfreiche Beobachtung von @ cardboard_box verwendet, dass keine Duplikate im generierenden Array berücksichtigt werden müssen.

In meinem Code

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

erzeugt alle Kombinationen der Zahlen 1 bis a.max, die ngleichzeitig (wo a.max = a.last = a[-1]) genommen werden. Für jede Kombination 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}

Füllt einen Hash hmit allen Zahlen, die Summen über nicht leere Teilmengen von sind b. Die Hash-Schlüssel sind diese Zahlen; Die Werte sind beliebig. (Ich habe letzteres auf Null gesetzt.)

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

prüft, ob jedes Element des angegebenen Arrays aein Schlüssel im Hash ist ( h[e] != niloder nur h[e]).

Annehmen

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

Dann iterieren wir über den Bereich:

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

Die binäre Darstellung jeder Zahl in diesem Bereich wird verwendet, um die Elemente der bSumme zu erstechen . Für j = 3( jals Bereichsindex),

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 

Der Code:

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
quelle