Math operation

Hello,

Expecting only fraction syntax as a result, I would like to generate all the possible combinations from those three variables :

n = number of items in the array;
y = sum of those items in the array;
x = the divisor under the fraction;

For example:
n = 3;
y = 4;
x = 5;

expected result : [ [4/5, 8/5, 8/5], [2/5, 10/5, 8/5], etc...] //  ( all possible combinations in array format)

This is scratching my brain, how to do that ?

Thanks

As stated, that’s an infinite set.

Are you okay with these additional constraints?

  • Whole numbers
  • Positive
  • Zero is allowed

Whole number and positive yes, but zero has to be not allowed cause it’s gone be use to generate time patterns

This is ugly as all hell and probably not the most efficient, but I think it works:

(
~func = {arg n, y, x;
    if(n == 1){
        [y]
    }{
        (1..(y * x + 1 - n)).collect{|i|
            ~func.(n - 1, y - (i / x), x).collect{|j|
                [i / x] ++ j
            }.flat.clump(n)
        }.flatten(1)
    }
};

~func.(3, 4, 5);
)

I’ll leave it to you to convert the results to fractional representation

That’s a slight variation of the classical problem of integer partition. You can do it with miSCellaneous_lib’s enum method. See this recent thread for an explanation of enum’s working: Turning interval sets into series of adjacent interval pitches

The integer partition is described in enum’s helpfile example #3. The only slight change you need is to multiply the sum by the denominator 5. Then the results would have to be divided by 5. There are two variants, depending if you want to distinguish ordering or not.

Adapted helpfile example #3:


(
// Function to make boolean value Function depending on sum a and number of summands n
g = { |a,n,ascending = true|
    var p = 0!(n+1);
    { |x,i,col| 
        var order = ((i > 0) && ascending).if { x >= col[i-1] }{ true };
        p[i+1] = p[i] + x;
        order and: {
            (i + 1 < n).if {
                // check if partial sums are not too large
                ascending.if { x }{ 1 } * (n - i) + p[i] <= a
            }{
                // partition check at last index i == n-1
                p[i+1] == a 
            }
        }
    }
};

// Function for listing all partitions of number a with n summands 

h = { |a,n,pool,ascending = true| n.enum(pool, g.(a,n,ascending), true) };
)


// partitions of number 20 consisting of 3 summands
// monotone tuples are demanded (so reorder of tuples is neglected)

h.(4 * 5, 3, (1..4 * 5)).postcs

-> [ [ 1, 1, 18 ], [ 1, 2, 17 ], [ 1, 3, 16 ], [ 1, 4, 15 ], [ 1, 5, 14 ], [ 1, 6, 13 ], [ 1, 7, 12 ], [ 1, 8, 11 ], [ 1, 9, 10 ], [ 2, 2, 16 ], [ 2, 3, 15 ], [ 2, 4, 14 ], [ 2, 5, 13 ], [ 2, 6, 12 ], [ 2, 7, 11 ], [ 2, 8, 10 ], [ 2, 9, 9 ], [ 3, 3, 14 ], [ 3, 4, 13 ], [ 3, 5, 12 ], [ 3, 6, 11 ], [ 3, 7, 10 ], [ 3, 8, 9 ], [ 4, 4, 12 ], [ 4, 5, 11 ], [ 4, 6, 10 ], [ 4, 7, 9 ], [ 4, 8, 8 ], [ 5, 5, 10 ], [ 5, 6, 9 ], [ 5, 7, 8 ], [ 6, 6, 8 ], [ 6, 7, 7 ] ]


// with reorderings

h.(4 * 5, 3, (1..4 * 5), false).postcs 

-> [ [ 1, 1, 18 ], [ 1, 2, 17 ], [ 1, 3, 16 ], [ 1, 4, 15 ], [ 1, 5, 14 ], [ 1, 6, 13 ], [ 1, 7, 12 ], [ 1, 8, 11 ], [ 1, 9, 10 ], [ 1, 10, 9 ], [ 1, 11, 8 ], [ 1, 12, 7 ], [ 1, 13, 6 ], [ 1, 14, 5 ], [ 1, 15, 4 ], [ 1, 16, 3 ], [ 1, 17, 2 ], [ 1, 18, 1 ], [ 2, 1, 17 ], [ 2, 2, 16 ], [ 2, 3, 15 ], [ 2, 4, 14 ], [ 2, 5, 13 ], [ 2, 6, 12 ], [ 2, 7, 11 ], [ 2, 8, 10 ], [ 2, 9, 9 ], [ 2, 10, 8 ], [ 2, 11, 7 ], [ 2, 12, 6 ], [ 2, 13, 5 ], [ 2, 14, 4 ], [ 2, 15, 3 ], [ 2, 16, 2 ], [ 2, 17, 1 ], [ 3, 1, 16 ], [ 3, 2, 15 ], [ 3, 3, 14 ], [ 3, 4, 13 ], [ 3, 5, 12 ], [ 3, 6, 11 ], [ 3, 7, 10 ], [ 3, 8, 9 ], [ 3, 9, 8 ], [ 3, 10, 7 ], [ 3, 11, 6 ], [ 3, 12, 5 ], [ 3, 13, 4 ], [ 3, 14, 3 ], [ 3, 15, 2 ], [ 3, 16, 1 ], [ 4, 1, 15 ], [ 4, 2, 14 ], [ 4, 3, 13 ], [ 4, 4, 12 ], [ 4, 5, 11 ], [ 4, 6, 10 ], [ 4, 7, 9 ], [ 4, 8, 8 ], [ 4, 9, 7 ], [ 4, 10, 6 ], [ 4, 11, 5 ], [ 4, 12, 4 ], [ 4, 13, 3 ], [ 4, 14, 2 ], [ 4, 15, 1 ], [ 5, 1, 14 ], [ 5, 2, 13 ], [ 5, 3, 12 ], [ 5, 4, 11 ], [ 5, 5, 10 ], [ 5, 6, 9 ], [ 5, 7, 8 ], [ 5, 8, 7 ], [ 5, 9, 6 ], [ 5, 10, 5 ], [ 5, 11, 4 ], [ 5, 12, 3 ], [ 5, 13, 2 ], [ 5, 14, 1 ], [ 6, 1, 13 ], [ 6, 2, 12 ], [ 6, 3, 11 ], [ 6, 4, 10 ], [ 6, 5, 9 ], [ 6, 6, 8 ], [ 6, 7, 7 ], [ 6, 8, 6 ], [ 6, 9, 5 ], [ 6, 10, 4 ], [ 6, 11, 3 ], [ 6, 12, 2 ], [ 6, 13, 1 ], [ 7, 1, 12 ], [ 7, 2, 11 ], [ 7, 3, 10 ], [ 7, 4, 9 ], [ 7, 5, 8 ], [ 7, 6, 7 ], [ 7, 7, 6 ], [ 7, 8, 5 ], [ 7, 9, 4 ], [ 7, 10, 3 ], [ 7, 11, 2 ], [ 7, 12, 1 ], [ 8, 1, 11 ], [ 8, 2, 10 ], [ 8, 3, 9 ], [ 8, 4, 8 ], [ 8, 5, 7 ], [ 8, 6, 6 ], [ 8, 7, 5 ], [ 8, 8, 4 ], [ 8, 9, 3 ], [ 8, 10, 2 ], [ 8, 11, 1 ], [ 9, 1, 10 ], [ 9, 2, 9 ], [ 9, 3, 8 ], [ 9, 4, 7 ], [ 9, 5, 6 ], [ 9, 6, 5 ], [ 9, 7, 4 ], [ 9, 8, 3 ], [ 9, 9, 2 ], [ 9, 10, 1 ], [ 10, 1, 9 ], [ 10, 2, 8 ], [ 10, 3, 7 ], [ 10, 4, 6 ], [ 10, 5, 5 ], [ 10, 6, 4 ], [ 10, 7, 3 ], [ 10, 8, 2 ], [ 10, 9, 1 ], [ 11, 1, 8 ], [ 11, 2, 7 ], [ 11, 3, 6 ], [ 11, 4, 5 ], [ 11, 5, 4 ], [ 11, 6, 3 ], [ 11, 7, 2 ], [ 11, 8, 1 ], [ 12, 1, 7 ], [ 12, 2, 6 ], [ 12, 3, 5 ], [ 12, 4, 4 ], [ 12, 5, 3 ], [ 12, 6, 2 ], [ 12, 7, 1 ], [ 13, 1, 6 ], [ 13, 2, 5 ], [ 13, 3, 4 ], [ 13, 4, 3 ], [ 13, 5, 2 ], [ 13, 6, 1 ], [ 14, 1, 5 ], [ 14, 2, 4 ], [ 14, 3, 3 ], [ 14, 4, 2 ], [ 14, 5, 1 ], [ 15, 1, 4 ], [ 15, 2, 3 ], [ 15, 3, 2 ], [ 15, 4, 1 ], [ 16, 1, 3 ], [ 16, 2, 2 ], [ 16, 3, 1 ], [ 17, 1, 2 ], [ 17, 2, 1 ], [ 18, 1, 1 ] ]

You could also include the division right away:

h.(4 * 5, 3, (1..4 * 5)) / 5
// Quarks.install("Rational")
f = { |n, sum, den, min=1, max=10|
    var l, range, scaled; 
    scaled = sum*den;
    range = (min..min(scaled, max*den))!n;
    l = all {:tuple,
        tuple <- range.allTuples,
        tuple.sum == scaled
    };   
    l.deepCollect(2, {|x| x %/ den})
};


a = f.(3, 4, 5);
b = f.(3, 4, 5, 5);

a.size; 
b.size; 
a every:  {|i| i.size == 3 and: i.sum  == 4 }; // true
b every:  {|i| i.size == 3 and: i.sum  == 4 }; // true