Zahl in etwa gleiche Werte beliebiger Anzahl aufteilen

  • VB.NET
  • .NET (FX) 4.0

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Zahl in etwa gleiche Werte beliebiger Anzahl aufteilen

    Hallo,

    ich möchte einen variablen Wert von etwa 6 bis 15 in minimal 3 und maximal 4 aufteilen.

    zum Beispiel:
    7 ergibt 4;3
    9 ergibt 3;3;3
    10 ergibt 4;3;3
    14 ergibt 4:4:3:3

    Folgendes habe ich dafür bisher ausgetüftelt, was auch ganz gut funktioniert.

    VB.NET-Quellcode

    1. Dim LaufNr As Integer = 1
    2. Dim NextLauf As Integer = 4
    3. If (DataGridView1.Rows.Count - 1) Mod 4 = 0 OrElse (DataGridView1.Rows.Count - 2) Mod 4 = 0 Then
    4. NextLauf = 3
    5. End If
    6. For I As Integer = 0 To DataGridView1.Rows.Count - 1
    7. DataGridView1.Rows(I).Cells(1).Value = LaufNr ' Laufnummer setzen
    8. If (I + 1) Mod NextLauf = 0 AndAlso I + 1 < DataGridView1.Rows.Count - 1 Then
    9. LaufNr += 1
    10. End If
    11. Next


    Damit wird aber leider nicht immer der höhere Wert nach hinten gesetzt, wie ich es in der Ausgabe gern hätte.
    Gibt es dafür eine einfache Möglichkeit?
    @prinzip Das ist eine Diophantische Gleichung.
    Schau da mal rein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Das ist eine Diophantische Gleichung.

    Oh oh, da habe ich mir ja was vorgenommen. Ich muss gestehen, dass ich in Mathe eher eine -0 bin. =O ;)
    Im beigefügten Link sehe ich tatsächlich irgendwie den Wald vor lauter Bäume nicht. Grundsätzlich funktioniert meine Bastelei ja, eben nicht optimal.

    Es geht übrigens darum, 6-15 Sportler (DataGridView1.Rows.Count) auf Läufe (LaufNr) mit 3-4 Läufern (NextLauf) aufzuteilen, wobei die höher besetzten Läufe möglichst an den Anfang gesetzt werden sollte. Das hatte ich im Eingangspost gar nicht erwähnt, ist aber eventuell dem Code zu entnehmen. :|

    Ich danke Dir aber für das Stichwort zur Thematik.
    Ich würde das so machen:
    1. durch 4 Teilen (=also jeweils 4 in eine Gruppe, bis nicht mehr genug da sind) und dann Fallunterscheidung:
    Wenn Rest 0 dann fertig,
    Wenn Rest 3 dann fertig,
    Wenn Rest 2 einen aus der vorletzten Gruppe in die letzte und fertig
    Wenn Rest 1 jeweils einen aus der vorletzten & vorvorletzen in die letzte und fertig.

    Es ergibt sich automatisch, dass die 4er-Läufe am Anfang stehen.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    siehe hier

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Public Module Module1
    4. Public Sub Main()
    5. Bsp2()
    6. End Sub
    7. Private Sub Bsp2()
    8. Dim start As Int32 = 3
    9. Dim finish As Int32 = 4
    10. Dim maxsum As Int32 = 15
    11. Dim res As New List(Of Int32())
    12. Dim r = Enumerable.Range(start, finish - start + 1).OrderByDescending(Function(x) x)
    13. Dim range = New List(Of Integer)(r)
    14. For i As Int32 = start To maxsum
    15. res.Add(Calc(range.ToList, New Int32(range.Count - 1) {}, i, 0))
    16. Next
    17. PrintOut(res.ToArray, range.ToArray, start)
    18. Console.ReadKey()
    19. End Sub
    20. Private Function Calc(ByRef range As List(Of Int32), _
    21. ByRef result As Int32(), _
    22. ByVal summe As Int32, _
    23. ByVal inc As Int32) As Int32()
    24. If inc >= range.Count Then range.Clear() : Return range.ToArray
    25. Dim rest As Int32 = summe Mod range(inc)
    26. Dim anzahl As Int32 = summe \ range(inc)
    27. result(inc) = anzahl
    28. Return If(rest = 0, result, Calc(range, result, rest, inc + 1))
    29. End Function
    30. Private Sub PrintOut(ByVal iArr()() As Int32, ByVal range() As Int32, ByVal start As Int32)
    31. If iArr IsNot Nothing AndAlso iArr.Length > 0 Then
    32. For i As Int32 = 0 To iArr.Length - 1
    33. If iArr(i).Length > 0 Then
    34. Console.Write("Summe:{0} {1}", start + i, vbTab)
    35. For j As Int32 = 0 To iArr(i).Length - 1
    36. For k As Int32 = 0 To iArr(i)(j) - 1
    37. Console.Write(range(j) & " ")
    38. Next
    39. Next
    40. End If
    41. If iArr(i).Length > 0 Then Console.WriteLine()
    42. Next
    43. End If
    44. End Sub
    45. End Module


    Edit: Kleiner Fehler ist noch drin. Wenn ich morgen Zeit habe, werde ich es kurz machen.

    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    prinzip schrieb:


    Hier wird jedoch eine zuvor gesetzte Reihenfolge nach bisheriger Leistung zerstört. :/


    Es gibt zig allgemein anerkannte Algorithmen, die ein bisher erarbeitetes Zwischenergebnis weiter optimieren, von der Zerstörung der bisherigen Leistung spricht da niemand.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Vielen dank @exc-jdbi, ich versuche es mal im einzelnen zu verstehen und den Nutzen daraus zu ziehen.
    Für meine Verwendung müsste ich dann die Ziffernfolge (z.B. 4,3,3,3 für Summe 13) in einen Array packen und in der Schleife in meinem Schnipsel die Einzelwerte "NextLauf" zuweisen.
    Im Moment begreife ich in deiner Routine noch nicht mal wirklich (auch mit F8), warum nur die für die Summe 3,4 ; 7,8 ; 11,12 ; 15,16 und 19,20 mit einem Abstand von 3 die Aufteilung ausgegeben wird. Das hängt sicherlich mit den Werten von 'start' und 'finish' zusammen, die da wohl ungewollt als Schrittzähler zum Einsatz kommen.

    exc-jdbi schrieb:

    Kleiner Fehler ist noch drin. Wenn ich morgen Zeit habe, werde ich es kurz machen.

    Bezieht sich der kleine Fehler auf diesen "Schrittzähler"
    So noch kurz Zeit gefunden. Ist sicher nicht die eleganteste Lösung, aber sie funkst sicher.
    Den NSC kriegst von hier.

    Lösung

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Text
    4. Public Module Module1
    5. Public Sub Main()
    6. Dim start As Int32 = 3
    7. Dim finish As Int32 = 5
    8. Dim maxsum As Int32 = 20
    9. Dim res As New List(Of Int32())
    10. Dim r = Enumerable.Range(start, finish - start + 1).OrderByDescending(Function(x) x)
    11. Dim range = New List(Of Integer)(r)
    12. Dim resultlist() As String
    13. For i As Int32 = start To maxsum
    14. resultlist = GetResultList(range, i)
    15. PrintOut(resultlist, range.ToArray, i)
    16. Next
    17. Console.ReadLine()
    18. End Sub
    19. Private Function GetResultList(ByVal range As List(Of Integer), ByVal maxsum As Int32) As String()
    20. Dim s As Int32
    21. Dim splitter() As String
    22. Dim res As New List(Of String)
    23. Dim maxlen = GetMaxLen(range, maxsum)
    24. Dim rangelist = GetRangeList(range.Count, maxlen)
    25. For i As Int32 = 0 To rangelist.Length - 1
    26. s = 0
    27. splitter = rangelist(i).Split({"-"c}, StringSplitOptions.RemoveEmptyEntries)
    28. For j As Int32 = 0 To splitter.Length - 1
    29. s += ((range(j) * CInt(splitter(j).ToString)))
    30. Next
    31. If s = maxsum Then
    32. res.Add(rangelist(i))
    33. End If
    34. Next
    35. Return res.ToArray
    36. End Function
    37. Private Function GetMaxLen(ByRef range As List(Of Integer), ByVal maxsum As Int32) As Int32
    38. Dim maxlen As Int32 = 0
    39. For i As Int32 = 0 To range.Count - 1
    40. maxlen = Math.Max(maxlen, maxsum \ range(i))
    41. Next
    42. Return maxlen
    43. End Function
    44. Private Sub PrintOut(ByVal reslist() As String, ByVal range() As Int32, ByVal maxsum As Int32)
    45. Dim splitter() As String
    46. For Each s As String In reslist
    47. Console.Write("Sum:{1}{0}{1}", maxsum, vbTab)
    48. splitter = s.Split({"-"c}, StringSplitOptions.RemoveEmptyEntries)
    49. For i As Int32 = 0 To splitter.Length - 1
    50. If CInt(splitter(i)) = 0 Then Continue For
    51. For j As Int32 = 0 To CInt(splitter(i)) - 1
    52. Console.Write("{0} ", range(i))
    53. Next
    54. Next
    55. Console.WriteLine()
    56. Next
    57. End Sub
    58. Private Function GetRangeList(ByVal len As Int32, ByVal maxlen As Int32) As String()
    59. Dim n As New NSC
    60. Dim startBase As Int32 = 10
    61. Dim targetBase As Int32 = maxlen + 1
    62. Dim goal = MakeGoal(len, maxlen)
    63. Dim inc As Int32 = 0
    64. Dim digit = CInt(Int(Math.Log10(maxlen)) + 1)
    65. Dim res As New List(Of String)
    66. While True
    67. inc += 1
    68. Dim iNum = makeArrayNumInt(inc.ToString)
    69. Dim resi() As Int32 = n.convert(iNum, startBase, targetBase)
    70. Dim str = makeArrayNum(resi, len)
    71. If Not res.Contains(str) Then
    72. res.Add(str)
    73. End If
    74. If resi.SequenceEqual(goal) OrElse inc > Int32.MaxValue - 100 Then
    75. Return res.ToArray
    76. End If
    77. End While
    78. Return Nothing
    79. End Function
    80. Private Function makeArrayNum(ByVal b() As Integer, ByVal len As Int32) As String
    81. Dim arr = New Int32(len - 1) {}
    82. Array.Copy(b, 0, arr, len - b.Length, b.Length)
    83. Return String.Join("-", arr.Select(Function(x) x.ToString).ToArray)
    84. 'Dim inc As Int32 = 0
    85. 'If arr.Length > b.Length Then
    86. ' For i As Int32 = (arr.Length - b.Length) To arr.Length - 1
    87. ' arr(i) = b(inc) : inc += 1
    88. ' Next
    89. 'Else : Array.Copy(b, arr, b.Length)
    90. 'End If
    91. 'Return String.Join("-", arr.Select(Function(x) x.ToString).ToArray)
    92. End Function
    93. Private Function MakeGoal(ByVal len As Int32, ByVal maxlen As Int32) As Int32()
    94. If len > 0 AndAlso maxlen > 0 Then
    95. Dim res As New List(Of Int32)
    96. For i As Int32 = 0 To len - 1
    97. res.Add(maxlen)
    98. Next
    99. Return res.ToArray
    100. End If
    101. Return Nothing
    102. End Function
    103. Private Function makeArrayNumInt(ByVal s As String) As Integer()
    104. Return Encoding.UTF8.GetBytes(s).Select(Function(b) b - 48).ToArray()
    105. End Function
    106. End Module


    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „exc-jdbi“ ()

    exc-jdbi schrieb:

    kurz Zeit gefunden

    Ich bin beeindruckt, was Du so mal eben innerhalb kurzer Zeit hin bekommst. :thumbup:
    Das funktioniert einwandfrei für die Auflistungen und stellt mich vor ein neues Problem, welches ich noch gar nicht beachtet habe. Selbst in meiner ursprünglichen Limitierung auf 3 oder 4 gibt es bei 12 zwei Möglichkeiten. Entsprechend muss ich dann wohl doch besser fix entscheiden, welche Variante zum Einsatz kommt, zumal 10 wohl tatsächlich in 2 x 5 aufgeteilt werden soll.

    Die Lösung wird dann wohl sein, dass ich mittels ​select case entsprechend der Teilnehmerzahl einen Array passend befülle, welcher dann in einer Schleife die Laufzuordnungen passend setzt. Für zehn feste Varianten (6-15 Teilnehmer) ist das wohl die beste Lösung.

    Vielen Dank nochmal für die Umfangreiche Unterstützung. Bei Bedarf werde ich sicher darauf zurückgreifen. :)

    BTW: Mich wundert, dass Du viel mit Int32 arbeitest. Ich habe bisher ausschließlich mit Integer gearbeitet. Ist das in VB.NET nicht das gleiche? Bei so kleinen Werten sollte es damit doch auch keine Problem geben.

    exc-jdbi schrieb:

    dass Integer von Int32 abgeleitet ist.
    Nicht ganz.
    Der .NET-Datentyp heißt System.Int32.
    In VB.NET kann dafür synonym Integer verwendet werden, in C# int.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!