Integer mit spezifischem Bitset generieren

  • C#
  • .NET (FX) 1.0–2.0

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Integer mit spezifischem Bitset generieren

    Moin,
    Also, da der Titel vllt etwas unklar ist, versuche ich mein Vorhaben hier mal etwas genauer zu erklären :)
    Ich möchte eine Funktion coden, welche einen byte als input Parameter schluckt.
    Nennen wir diesen Parameter n.
    Entsprechend des Input parameters n, welcher Maximal 32 betragen kann, und minimal 0 sein darf, soll eine Zahl im Bereich 0 - UInt32.Maxvalue zufällig ausgewählt werden
    welche n bits gesetzt hat.
    Beispiel:



    Wäre als input parameter n die Zahl 6 gewählt worden, wäre 1337 ein möglicher Output, da dieser eben 6 Einsen bzw 6 gesetzte bits enthält.
    Genauso könnte auch als Output z.B. die Zahl 2230 returned werden da diese ebenfalls 6 bits enthält:



    Also kurz erklärt, ich möchte eine zufällig Zahl zwischen 0 und UInt32.MaxValue finden welche n gesetzte bits (angegeben durch den Input parameter) enhält.
    Wie mach ich das am sinnvollsten?
    C# Developer
    Learning C++
    Ich hab mal was zusammengebastelt. Ist zwar VB, aber das Konvertieren sollte kein Problem sein.

    VB.NET-Quellcode

    1. Dim Rnd As New Random
    2. Private Function GetRanomUInt32(HighBitCount As Integer) As UInt32
    3. 'Ungültige Werte abfangen
    4. If HighBitCount < 0 OrElse HighBitCount > 32 Then
    5. Throw New ArgumentOutOfRangeException("HighBitCount")
    6. End If
    7. 'Corner Cases abfangen
    8. If HighBitCount = 0 Then
    9. Return 0
    10. End If
    11. If HighBitCount = 32 Then
    12. Return UInt32.MaxValue
    13. End If
    14. Dim AvailableIndices = Enumerable.Range(0, 32).ToList 'Alle möglichen Indices von 0 bis 32 - 1
    15. Dim Result As UInt32 = 0
    16. For i = 1 To HighBitCount
    17. Dim Index = AvailableIndices(Rnd.Next(0, AvailableIndices.Count)) 'Zufällig einen möglichen Index auswählen
    18. Result = Result Or (1UI << Index) 'Das Bit an dem Index auf 1 setzen.
    19. AvailableIndices.Remove(Index) 'Und den Index anschließend entfernen, sodass er nicht mehr verwendet werden kann.
    20. Next
    21. Return Result
    22. End Function

    Funktionieren tut das so: In AvailableIndices ist eine Liste aller möglichen Indices der Bits (also eine Liste von 0 bis 31). Bei jedem Durchlauf wird ein zufälliger Index ausgewählt. Das Bit an dem Index wird auf 1 gesetzt und der Index wird aus der Liste entfernt. Dadurch kann ein Index nie zweimal ausgewählt werden.
    Eine mögliche Optimierung wäre das:

    VB.NET-Quellcode

    1. Dim IndexIndex = Rnd.Next(0, AvailableIndices.Count) 'Zufällig einen möglichen Index auswählen
    2. Dim Index = AvailableIndices(IndexIndex)
    3. Result = Result Or (1UI << Index) 'Das Bit an dem Index auf True setzen.
    4. AvailableIndices.RemoveAt(IndexIndex) 'Und den Index anschließend entfernen, sodass er nicht mehr verwendet werden kann.
    Dadurch muss nicht erst nach dem Index in der Liste gesucht werden, weil man weiß ja schon, wo der sein muss (IndexIndex).
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    Ich hab mal was zusammengebastelt. Ist zwar VB, aber das Konvertieren sollte kein Problem sein.

    VB.NET-Quellcode

    1. Dim Rnd As New Random
    2. Private Function GetRanomUInt32(HighBitCount As Integer) As UInt32
    3. 'Ungültige Werte abfangen
    4. If HighBitCount < 0 OrElse HighBitCount > 32 Then
    5. Throw New ArgumentOutOfRangeException("HighBitCount")
    6. End If
    7. 'Corner Cases abfangen
    8. If HighBitCount = 0 Then
    9. Return 0
    10. End If
    11. If HighBitCount = 32 Then
    12. Return UInt32.MaxValue
    13. End If
    14. Dim AvailableIndices = Enumerable.Range(0, 32).ToList 'Alle möglichen Indices von 0 bis 32 - 1
    15. Dim Result As UInt32 = 0
    16. For i = 1 To HighBitCount
    17. Dim Index = AvailableIndices(Rnd.Next(0, AvailableIndices.Count)) 'Zufällig einen möglichen Index auswählen
    18. Result = Result Or (1UI << Index) 'Das Bit an dem Index auf 1 setzen.
    19. AvailableIndices.Remove(Index) 'Und den Index anschließend entfernen, sodass er nicht mehr verwendet werden kann.
    20. Next
    21. Return Result
    22. End Function

    Funktionieren tut das so: In AvailableIndices ist eine Liste aller möglichen Indices der Bits (also eine Liste von 0 bis 31). Bei jedem Durchlauf wird ein zufälliger Index ausgewählt. Das Bit an dem Index wird auf 1 gesetzt und der Index wird aus der Liste entfernt. Dadurch kann ein Index nie zweimal ausgewählt werden.
    Eine mögliche Optimierung wäre das:

    VB.NET-Quellcode

    1. Dim IndexIndex = Rnd.Next(0, AvailableIndices.Count) 'Zufällig einen möglichen Index auswählen
    2. Dim Index = AvailableIndices(IndexIndex)
    3. Result = Result Or (1UI << Index) 'Das Bit an dem Index auf True setzen.
    4. AvailableIndices.RemoveAt(IndexIndex) 'Und den Index anschließend entfernen, sodass er nicht mehr verwendet werden kann.
    Dadurch muss nicht erst nach dem Index in der Liste gesucht werden, weil man weiß ja schon, wo der sein muss (IndexIndex).

    Da können aber auch negative Zählen auftreten,oder?
    C# Developer
    Learning C++
    negative Zählen

    Huch? Wie das?
    UInt32 ist ein Unsigned Integer mit 32 Bits. Das VB-Alias wäre UInteger. Das C#-Alias uint.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    ~blaze~ schrieb:

    Hi
    das geht noch wesentlich eleganter:
    Nimm für ein Integer, das n zufällig gesetzte Bits haben soll, ein Integer, das bereits n gesetzte Bits hat (ohne Zufall) und vertausche einfach 32 mal je zwei zufällig gewählte Bits miteinander.
    Bzw. ggf. reichen auch 16 Durchläufe.

    Viele Grüße
    ~blaze~

    Heißt das ich brauch 31 "Beispiel-Ints" welche mir als Referenz dienen?
    Und was meinst du mit 16 Durchläufen, kannst du das bitte nochmal genauer erläutern?:)
    C# Developer
    Learning C++

    tolio schrieb:

    C#-Quellcode

    1. var rnd = new Random();
    2. var count = 6;
    3. var result = 0;
    4. Enumerable.Range(0, count).ToList().ForEach((x) => { var i = 0; do { i = 1 << rnd.Next(0, 32); } while ((result & i) > 0); result |= i; });
    Deine Lösung funktioniert auch sehr gut, allerdings benötigt das ganze Linq (FW 3.0+).Wie schreib ich das am besten um so dasses auch für FW 2.0 funzt? ;D


    ErfinderDesRades schrieb:

    also mein Vorschlag wäre:
    1. mach dir ein Array mit 32 Booleans
    2. setze 6 davon auf True, den Rest anders
    3. MIsche das Array gut durch
    4. durchlaufe das Array, und bei True setze entsprechend das Bit eines Integer - ungefähr wie von Niko gezeigt.


    1) Done.
    2) Done.
    3) Done.
    4) Wie mach ich das jetzt, ich verstehs nich ganz.

    C#-Quellcode

    1. private static UInt32 GenerateSpecificBitInteger(int bits)
    2. {
    3. bool[] arr = new bool[32];
    4. for (int i = 0; i < 6; i++)
    5. arr[i] = true;
    6. int n = arr.Length;
    7. for (int i = 0; i < n; i++)
    8. {
    9. int r = i + (int)(Rnd.NextDouble() * (n - i));
    10. bool t = arr[r];
    11. arr[r] = arr[i];
    12. arr[i] = t;
    13. }
    14. for (int i = 0; i < n; i++)
    15. {
    16. // Hm, ... wie die Bits setzen
    17. }
    18. }
    C# Developer
    Learning C++
    wie man ein bit an einem Index setzt, hat Niko doch gezeigt - ist daran iwas unklar?

    VB.NET-Quellcode

    1. Result = Result Or (1UI << Index)
    tipp: dein i ist der Index, und mit einem if(arr(i)) prüfst du, ob das bit zu setzen ist.

    (Übrigens mir was, als hätte ich dir schonmal empfohlen, als kleine Erleichterung für so Sachen eine .Swap-Extension einzuführen.)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Das Array würfeln geht übrigens einfacher... bzw. weiß ich nicht, ob das mitm .Net Framework 2.0 auch geht.

    VB.NET-Quellcode

    1. Dim Rnd As New Random
    2. Function F(HighBitCount As Integer) As UInt32
    3. 'Ungültige Werte abfangen
    4. If HighBitCount < 0 OrElse HighBitCount > 32 Then
    5. Throw New ArgumentOutOfRangeException("HighBitCount")
    6. End If
    7. 'Corner Cases abfangen
    8. If HighBitCount = 0 Then
    9. Return 0
    10. End If
    11. If HighBitCount = 32 Then
    12. Return UInt32.MaxValue
    13. End If
    14. 'Array erstellen
    15. Dim Bits = New Boolean(32 - 1) {}
    16. For i = 0 To HighBitCount - 1
    17. Bits(i) = True
    18. Next
    19. 'Array durcheinanderwürfeln
    20. Array.Sort(Bits, Function(Left, Right) If(Rnd.NextDouble < 0.5, -1, 1))
    21. 'Integer zusammenbasteln
    22. Dim Result As UInt32 = 0
    23. For i = 0 To 31
    24. If Bits(i) Then
    25. Result = Result Or (1UI << i)
    26. End If
    27. Next
    28. Return Result
    29. End Function


    PS:

    Huch? Wie das?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    ja, aber Fisher-Yates ist vielfach performanter, ich nehme an, Rikudo will da was schnelles haben.

    edit:
    also glaub ich zumindest, aber wenn die Random-Funktion das überaus langsamte Element ist, dann wärs wohl egal, wenn man das eigliche Mischen der vergleichsweise aufwändigen Sort-Methode mit ihren Mehrfach-Durchgängen überantwortet.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Habs so gemacht (Danke an tolio) :

    C#-Quellcode

    1. private static uint GenerateSpecificBitInteger(uint n)
    2. {
    3. uint res = 0;
    4. for (int x = 0; x < n; x++)
    5. {
    6. uint i = Convert.ToUInt32((uint)1 << rnd.Next(0, 32));
    7. while ((res & i) > 0)
    8. i = Convert.ToUInt32((uint)1 << rnd.Next(0, 32));
    9. res = res | i;
    10. }
    11. return res;
    12. }
    C# Developer
    Learning C++
    Hi
    ggf. so?

    VB.NET-Quellcode

    1. Shared Function GenerateUInt32(n As Integer) As UInteger
    2. If n = 0 Then Return 0
    3. If n < 0 OrElse n > 32 Then Throw New ArgumentOutOfRangeException("n")
    4. Dim rnd As New Random()
    5. Dim result As UInteger = &HFFFFFFFFUI >> (32 - n) 'n Bits bleiben über
    6. For i As Integer = 1 To 32
    7. Dim j As Integer = rnd.Next(0, 32)
    8. '(result And Not ((1UI << i) Or (1UI << j))) entfernt je die beiden Werte, die vertauscht werden sollen
    9. '(((result >> x) And 1UI) << y) entspricht x nach y schieben
    10. result = (result And Not ((1UI << i) Or (1UI << j))) Or (((result >> i) And 1UI) << j) Or (((result >> j) And 1UI) << j)
    11. Next
    12. Return result
    13. End Function

    Ist wieder mal nicht getestet, da es sonst dem Faulheitsprinzip widerspricht, aber könnte mir vorstellen, dass es funktioniert.

    Viele Grüße
    ~blaze~
    *Vollzitat entfernt*


    Hey,
    Danke für deinen Ansatz.
    Mein versuch den nach C# umzuschreiben mit Converter + Hand:

    C#-Quellcode

    1. public static uint GenerateUInt32(int n)
    2. {
    3. if (n == 0)
    4. return 0;
    5. if (n < 0 || n > 32)
    6. throw new ArgumentOutOfRangeException("n");
    7. Random rnd = new Random();
    8. uint result = 0xffffffffu >> (32 - n);
    9. for (int i = 1; i <= 32; i++)
    10. {
    11. int j = rnd.Next(0, 32);
    12. result = (result & !((1u << i) | (1u << j))) | (((result >> i) & 1u) << j) | (((result >> j) & 1u) << j);
    13. }
    14. return result;
    15. }


    Operator '!' cannot be applied to operand of type 'uint'.

    C#-Quellcode

    1. ​result = (result & !((1u << i) | (1u << j))) | (((result >> i) & 1u) << j) | (((result >> j) & 1u) << j);

    Irgendwie sind die Klammern hier falsch, ich kriegs aber nicht gebacken sie richtig zu setzen, bzw den entsprechende typcast [?] durchzuführen?

    Edit by ~blaze~:
    *Vollzitat entfernt*
    C# Developer
    Learning C++

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()

    ~ ist das zugehörige Äquivalent in C#. ! ist's nur bei Boolean.
    Konvertierung von uint-Daten in int-Daten geht btw. einfach über unchecked((int)uintvalue)

    Der Algorithmus von tolio terminiert leider nicht zwangsweise und ist u.U. ineffizient, sonst wäre es ggf. schon praktisch, das so zu lösen (hatte ich aber aus genannten Gründen verworfen)

    Viele Grüße
    ~blaze~

    tolio schrieb:

    C#-Quellcode

    1. var rnd = new Random();
    2. var count = 6;
    3. var result = 0;
    4. while (count > 0)
    5. {
    6. var y = 1 << rnd.Next(0, 32);
    7. if (!((result & y) > 0))
    8. {
    9. result |= y;
    10. count--;
    11. }
    12. }

    Danke das funktioniert ebenfalls :)

    ~blaze~ schrieb:

    ~ ist das zugehörige Äquivalent in C#. ! ist's nur bei Boolean.
    Konvertierung von uint-Daten in int-Daten geht btw. einfach über unchecked((int)uintvalue)

    Der Algorithmus von tolio terminiert leider nicht zwangsweise und ist u.U. ineffizient, sonst wäre es ggf. schon praktisch, das so zu lösen (hatte ich aber aus genannten Gründen verworfen)

    Viele Grüße
    ~blaze~

    Dann funktioniert deine Funktion aber nicht ganz richtig ;) (oder ich habs falsch umgeschrieben? ;o )

    C#-Quellcode

    1. public static uint GenerateUInt32(int n)
    2. {
    3. if (n == 0)
    4. return 0;
    5. if (n < 0 || n > 32)
    6. throw new ArgumentOutOfRangeException("n");
    7. uint result = 0xffffffffu >> (32 - n);
    8. for (int i = 1; i < 32; i++)
    9. {
    10. int j = rnd.Next(0, 32);
    11. result = (result & ~((1u << i) | (1u << j))) | (((result >> i) & 1u) << j) | (((result >> j) & 1u) << j);
    12. }
    13. return result;
    14. }


    Jedenfalls haben die Outputs uints nicht immer n bits, sondern oftmals auch n-1, n-2 oder n-3 ;)
    C# Developer
    Learning C++