How to split a number N into M random positive parts whose sum is exactly N in VB.NET

1 Answer

0 votes
Imports System
Imports System.Collections.Generic

Module SplitRandomParts

    '
    '    Split a number N into M random positive parts that sum to N.
     
    '    Algorithm (uniform integer composition):
    '    ----------------------------------------
    '    1. We want M positive integers p1, p2, ..., pM such that:
    '           p1 + p2 + ... + pM = N
    ' 
    '    2. Generate (M - 1) random "cut points" in the range [1, N - 1].
    '       Example: N = 20, M = 4 → generate 3 cuts, e.g. {3, 11, 17}
    '       
    '    3. Sort the cut points.
    ' 
    '    4. The parts are the differences between consecutive cuts:
    '         p1 = cut[0]
    '         p2 = cut[1] - cut[0]
    '         ...
    '         pM = N - cut[M-2]
    ' 
    '    This produces a uniformly random composition of N into M parts.
    '

    '
    '    Important:
    '    -----------
    '    Random cut points must be UNIQUE.
    '    If two cut points are equal, their difference becomes 0,
    '    which produces an invalid part.
    '

    ' Generate M random positive parts summing to N
    Function SplitIntoRandomParts(N As Integer, M As Integer) As List(Of Integer)
        If M <= 0 OrElse N < M Then
            Throw New ArgumentException("Invalid N or M: require N >= M >= 1")
        End If

        Dim cuts As New List(Of Integer)(M - 1)
        Dim used As New HashSet(Of Integer)()  ' ensures uniqueness

        Dim rand As New Random()

        ' Generate UNIQUE cut points
        While cuts.Count < (M - 1)
            Dim c As Integer = 1 + rand.Next(N - 1)
            If Not used.Contains(c) Then
                used.Add(c)
                cuts.Add(c)
            End If
        End While

        ' Sort the cut points
        cuts.Sort()

        ' Build the parts
        Dim parts As New List(Of Integer)(M)

        Dim prev As Integer = 0
        For Each c As Integer In cuts
            parts.Add(c - prev)   ' guaranteed >= 1
            prev = c
        Next

        parts.Add(N - prev)       ' last part, also >= 1

        Return parts
    End Function

    Sub Main()
        Dim N As Integer = 30
        Dim M As Integer = 5

        Dim parts As List(Of Integer) = SplitIntoRandomParts(N, M)

        Console.WriteLine("Splitting N = " & N & " into M = " & M & " random parts:")
        For Each p As Integer In parts
            Console.Write(p & " ")
        Next
        Console.WriteLine()
    End Sub

End Module



' run:
' 
' Splitting N = 30 into M = 5 random parts:
' 15 1 3 4 7 
'

 



answered Jul 19 by avibootz

Related questions

...