2013-03-08 10 views
26

Was ist die effizienteste Methode, um den Wert von n zu bewerten wähle k? Die rohe Kraft, die ich denke, wäre, n faktorielle/k Fakultät/(n-k) Fakultät zu finden.Berechne den Wert von n wähle k

Eine bessere Strategie könnte sein, dp gemäß dieser recursive formula zu verwenden. Gibt es eine andere bessere Methode, um n zu bewerten, wählen Sie k?

+0

Faktorielle Berechnung ist viel effizienter als Ihre rekursive Alternative in Raum und Zeit – SomeWittyUsername

+2

Nun, für den Anfang können Sie ersetzen 'n!/K!' Mit 'n * (n-1) * (n-2) * ... * (k + 1) 'Es gibt keinen Punkt, wenn' n! 'und' k! 'vollständig berechnet werden, wenn sich viele der Faktoren ausgleichen. –

+0

Welchen Bereich von n erwägen Sie? –

Antwort

23

Sie konnten die Multiplikativ Formel für diesen Einsatz:

enter image description here

http://en.wikipedia.org/wiki/Binomial_coefficient#Multiplicative_formula

+14

wir können es beschleunigen, indem wir berechnen (n wählen n-k) statt (n wählen k) wenn n-k

+1

Berücksichtigen Sie, dass '(n - (ki))/i'keine ganze Zahl sein kann – pomber

+1

@pomber der einzelne' Faktor (n - (ki))/i' darf tatsächlich keine Ganzzahl sein, ABER das Produkt von die Faktoren von 'x = 1 bis zu y 'werden immer durch' y 'teilbar sein (weil es genau y aufeinanderfolgende ganze Zahlen gibt) – Lambder

-1

"Am effizientesten" ist eine schlechte Anfrage. Was versuchen Sie effizient zu machen? Der Stapel? Erinnerung? Geschwindigkeit? Insgesamt ist meine Meinung, dass die rekursive Methode am effizientesten ist, da sie nur Addition (eine billige Operation) verwendet und die Rekursion für die meisten Fälle nicht zu schlecht ist. Die Funktion ist:

nchoosek(n, k) 
{ 
    if(k==0) return 1; 
    if(n==0) return 0; 
    return nchoosek(n-1, k-1)+nchoosek(n-1,k); 
} 
+1

Das ist eine Baumrekursion und für große Werte für n und k wird es vielleicht gar nicht beendet. – Pedrom

+5

Es ist 'O (2^n)' Zeit und 'O (n)' Raum. Die faktorielle Berechnung ist "O (n)" Zeit und "O (1)" Raum. – SomeWittyUsername

+0

Ich stimme nicht zu. Dies ist effektiv Berechnung von Pascals Dreieck; es ist definitiv ** NICHT ** 'O (2^n)' - es ist 'O (n^2)'. Dies ist ein Quadrat, kein Baum. Außerdem wird es niemals überlaufen, wenn das Ergebnis lange gespeichert werden kann, und die Addition ist viel schneller als Multiplikation und Division. Noch mehr können Sie mit 'f (n, k) = f (n, n-k)' memotisieren, und alle Kantenfälle sind 1. –

0

Wenn Sie eine Lookup-Tabelle von factorials haben dann die Berechnung von C (n, k) wird sehr schnell sein.

+0

Für eine große Anzahl von n und k könnte diese Nachschlagetabelle prohibitiv sein. Außerdem sollte es eine Option für Werte außerhalb dieser Tabelle geben. – Pedrom

+0

@Pedrom Einschränkungen der Größe der Zahlen in der Frage wurden nicht erwähnt. Es ist mit "sprachagnotisch" und "Algorithmen" gekennzeichnet. –

0

Das Problem mit dem n!/k!(n-k)! Ansatz ist nicht so sehr die Kosten, da das Thema mit ! sehr schnell wachsenden, so daß auch für Werte von nCk, die im Rahmen von gut sind, sagen wir, 64-Bit-Integer, Zwischenberechnungen sind nicht. Wenn Sie nicht kainaw die rekursive Addition Ansatz wie könnten Sie die multiplikative Ansatz versuchen:

nCk == product(i=1..k) (n-(k-i))/i

wo product(i=1..k) das Produkt aller Begriffe bedeutet, wenn i die Werte 1,2,...,k nimmt.

+0

Sie haben recht mit der Möglichkeit, die Dinge überlaufen zu lassen, bevor die endgültige Antwort nicht mehr in ein Maschinenwort passt, aber ich mag Ihre Lösung nicht: Sie wird für einige Faktoren, z. für den i = 2-Faktor, wenn n = 4 und k = 3. (Natürlich multiplizieren sich die Faktoren am Ende zu einer ganzen Zahl, aber Ihr Weg bedeutet, dass Zwischenergebnisse in Gleitkommazahlen gespeichert werden müssen - yuck!) –

4

Wahrscheinlich die einfachste Möglichkeit, Binomialkoeffizienten (n choose k) ohne Überlauf zu berechnen, ist Pascal-Dreieck zu verwenden. Keine Brüche oder Multiplikationen sind notwendig. (n choose k). Die nth Zeile und die kth Eingabe von Pascals Dreieck geben den Wert an.

Take a look at this page. Dies ist eine O(n^2) Operation mit nur Addition, die Sie mit dynamischer Programmierung lösen können. Es wird blitzschnell für jede Zahl, die in eine 64-Bit-Ganzzahl passen kann.

+2

und O (n^2) zusätzlicher Platz – SomeWittyUsername

+0

@rici korrekt, aber das wäre eine Optimierung zu der vorgestellten Methode – SomeWittyUsername

+0

Ich weiß, es ist alt, aber das erfordert sicherlich nicht O (n^2) Raum. Ich habe es nur mit O (k) Space implementiert. Wird meine Lösung zeigen, wenn Sie möchten. –

2

Wenn Sie viele Kombinationen wie folgt berechnen gehen, die sicher die beste Option ist Triangle Pascal Berechnung. Wie Sie bereits die rekursive Formel wissen, ich glaube, ich kann hier vorbei einige Code:

MAX_N = 100 
MAX_K = 100 

C = [[1] + [0]*MAX_K for i in range(MAX_N+1)] 

for i in range(1, MAX_N+1): 
    for j in range(1, MAX_K+1): 
     C[i][j] = C[i-1][j-1] + C[i-1][j]; 

print C[10][2] 
print C[10][8] 
print C[10][3] 
35

Hier ist meine Version, die lediglich in ganzen Zahlen arbeitet (mit k die Division immer erzeugt einen ganzzahligen Quotienten) und ist schnell bei O (k):

function choose(n, k) 
    if k == 0 return 1 
    return (n * choose(n - 1, k - 1))/k 

ich schrieb es rekursiv, weil es so einfach ist und hübsch, aber man könnte es zu einer iterativen Lösung verwandeln, wenn Sie möchten.

+1

Es ist nicht O (* k *); * k * ist strikt kleiner als * n *, sodass Sie den Beitrag von * n * zur Laufzeit nicht ignorieren können. Im besten Fall können Sie sagen, dass es O (* k * M (* n *)) ist, wobei M (* n *) die Geschwindigkeit Ihres Multiplikationsalgorithmus ist. – chepner

+3

Richtig, aber pedantisch. Die oben angegebene Funktion macht O (k) Multiplikationen und Divisionen. Ich habe die Bit-Komplexität der Operationen selbst ignoriert. – user448810

+0

Diese Funktion berechnet 'n!/K!'. Das ist nicht, was die Frage ist – SomeWittyUsername

0

Der schnellste Weg ist wahrscheinlich, die Formel zu verwenden, und nicht pascals Dreieck. Lass uns anfangen, keine Multiplikationen zu machen, wenn wir wissen, dass wir später durch die gleiche Zahl teilen werden. Wenn k < n/2, haben wir k = n - k.Wir wissen, dass C (n, k) = C (n, n-k) Jetzt:

n!/(k! x (n-k)!) = (product of numbers between (k+1) and n)/(n-k)! 

Zumindest mit dieser Technik sind Sie nie durch eine Zahl dividiert, die Sie vor multiplizieren verwendet. Sie haben (n-k) Multiplikationen und (n-k) Divisionen.

Ich denke über einen Weg nach, alle Spaltungen zu vermeiden, indem wir GCDs zwischen den Zahlen finden, die wir multiplizieren müssen, und denen, die wir teilen müssen. Ich werde versuchen, später zu bearbeiten.

+0

Das Finden der GCD wird sicherlich die Menge der Operationen reduzieren. Leider wäre der GCD-Befund eine viel schwerere Aufgabe. – SomeWittyUsername

+0

Ja, ich habe Angst davor. Aber die GCDs würden auf kleinen Zahlen berechnet werden, wenn die Multiplikation eine große hat. Und eigentlich bin ich mir nicht sicher, dass eine GCD schwerer ist als eine Division. –

+0

Ich bin skeptisch, aber es wäre interessant, die Ergebnisse zu sehen :) – SomeWittyUsername

Verwandte Themen