2016-07-20 31 views
1

Vor diesem Posting habe ich mir diese beiden Antworten:Rundung auf die nächste Zahl in einer bestimmten Anordnung von Zahlen

C# Finding Nearest Number in Array

Round integer to nearest high number in array

Während die erste im Hinblick auf die Erweiterung ein wenig hilfreich war In einer der Antworten genannten Methode konnte ich sie nicht in der Tiefe verstehen, die es mir erlauben würde, sie zu modifizieren und zu implementieren, um sie meinen spezifischen Umständen anzupassen, und die zweite war nicht geeignet für meine Bedürfnisse aufgrund ihrer Anordnung von a kurze Länge und wenn ich ein Array verwenden sollte, um alle Basis 2-Nummern zu speichern Bis zu 268 Millionen würde es eine relativ große Anzahl von Einträgen haben.

Grundsätzlich, was ich hier versuche, ist RoundedSpeed ​​(die auf eine Ganzzahl mit Math.Round aufgerundet wurde) und runde es auf die nächste Zahl (es darf nie abrunden, nur nach oben), die das Ergebnis von ist 2. Beispielnummern umfassen 16384, 32768, 65536, 131072, etc.

Für dieses spezielle Beispiel ist die Nummer, auf die ich runden möchte, 32768. (Obwohl andere Methoden möglicherweise eine andere Nummer wünschen , aber das ist ein Thema, mit dem ich mich befassen muss und das nicht in den Anwendungsbereich dieses Beispiels fällt).

Hier ist der Code, den ich jetzt habe:

double Speed = Math.Pow(2, ((-900 + Time)/-60)) - 1; 
double RoundedSpeed = Math.Round(Speed); 

Ursprünglich suchte ich eine Reihe an mit den entsprechenden Nummern speichern abzurunden, aber gibt es eine andere Möglichkeit, ich RoundedSpeed zum nächsten bis abrunden könnte Basis 2 Ganzzahl?

+0

Für jeden, der LINQ vorschlagen würde, habe ich LINQ noch nicht gelernt, also bitte vermeiden Sie LINQ in der Antwort, es sei denn, absolut notwendig. Einige einfache Loops sind vielleicht nicht die eleganteste Lösung, aber ehrlich gesagt mache ich mir keine Sorgen um Code-Eleganz, ich möchte nur etwas, auf das ich später zurückkommen und verstehen kann, was es macht. – LC14199

+0

Sie möchten eine Zahl auf die nächste Potenz von 2 runden - richtig? – pm100

Antwort

4

Versuchen Sie, diese zu bekommen:

var x = 56; 
var y = (int)Math.Pow(2.0, Math.Ceiling(Math.Log(x, 2.0))); 

Ergebnisse in diesem Fall in 64.

effektiv dies ist die Bestimmung für n was 2^n = x, aber da n ist wahrscheinlich ein Bruchteil es Ceiling verwendet diese Nummer zu bringen, um die nächste ganze Zahl (oder es zu verlassen, wenn es bereits eine ganze Zahl ist). Also n2 = Ceiling(n). Dann berechnet es einfach 2^n2, um das Ergebnis y zu erhalten.

+0

++ Sie haben mich dazu geschlagen. Viel eleganter und einfacher Lösung. – GreatAndPowerfulOz

+0

Sehr elegant und ziemlich clever. Aber vielleicht würde OP von einer Erklärung profitieren, warum das funktioniert –

+0

^Damit einverstanden. Ich bin mir nicht ganz sicher, warum das funktioniert xD Obwohl etwas mir sagt, dass es mit dem Loggesetz von a (Loga (x)) = x zu tun hat. Ich werde das in meiner Formel testen und sehen, was es ausspuckt. Auch warum die var? Kennen wir den Datentyp nicht explizit? – LC14199

3

würde ich eine Schleife verwenden:

double RoundedSpeed = Math.Round(Speed); 
int maxAllowed = 100; // prevent infinite loop 
for (int i=1; i < maxAllowed; i++) 
{ 
    double number = Math.Pow(2.0, (double)i); 
    if (number > RoundedSpeed) 
     return number; 
} 

Wenn Sie nicht in einem Verfahren sind, die etwas zurückgibt, statt Rücksendenummer, etwas Sinnvolles tun mit der Nummer und fügen Sie dann „brechen“; um die Schleife zu stoppen.

Sie sollten eine Fehlerbehandlung hinzufügen, um sicherzustellen, dass Sie den maximalen Integer-Wert nicht überschreiten und etwas Nützliches tun, wenn Sie bis zu maxAllowed keine Zahl finden. Möglicherweise gibt es bessere Lösungen, aber dies ist eine "einfache" Lösung ...

+0

Ihr funktioniert gut, aber die Lösung von @ Enigmativity ist einfacher, eleganter und besser. IMMHO! – GreatAndPowerfulOz

+0

Ich würde argumentieren, dass die Berechnung von Log 2 viel langsamer wäre, aber ich weiß nicht, ob der Compiler Optimierungen durchführt, wenn man ihn in eine solche Decke einspeist: P – starlight54

3

Dies können Sie sehr effizient mit bitweisen Operationen tun. Ich habe ulong für die Eingabe und Ausgabe hier verwendet. Dies wird sehr schnell laufen.

ulong FancyRound(ulong value) { 
    value -= 1; 
    value |= value >> 1; 
    value |= value >> 2; 
    value |= value >> 4; 
    value |= value >> 8; 
    value |= value >> 16; 
    value |= value >> 32; 
    return value + 1; 
} 

Sie können es wie folgt verwenden:

for (ulong i = 1; i < 10; i++) { 
    Console.WriteLine("{0}: {1}", i, FancyRound(i)); 
} 

Ausgang:

1: 1 
2: 2 
3: 4 
4: 4 
5: 8 
6: 8 
7: 8 
8: 8 
9: 16 

Es funktioniert durch "Kaskadierung" alle gesetzten Bits nach unten, so dass nach dem alle | abgeschlossen sind, alle niedrigen Bits werden gesetzt, bis zum höchstwertigen gesetzten Bit. Danach wird das Ergebnis erhöht eine runde Potenz von 2

1

Ich würde es so tun, ich habe es nicht gebenchmarkt so werde ich keine Ansprüche über Geschwindigkeit machen:

long input = 32111; 
    long powerTwo = 1; 
    int count = 0; 
    int maxCount = 63; //You can't return 2^64 in a long so 63 should be the max here, as we start at 2^0! 
    while (input > powerTwo && count < maxCount) { 
     count++; 
     powerTwo <<= 1; 
    } 
    return powerTwo; 

Ausgänge 32.768, es funktioniert, weil eine lange (oder zumindest die untere Hälfte) wird im Speicher als 000000001 dargestellt (was 1 ist) und die <<= Operation verschiebt einfach die Bits links 1 und weist das Ergebnis zu, so dass es 000000010 (was 2 ist) für die nächste Iteration, dann wird einfach der Wert ausgeplottet bekam, als es größer war als die input.

Wenn Sie wissen, dass es vor einem Überlauf endet, dann können Sie den Zählkram entfernen.