Hi, I’m new to q and this is my first question here (I’m sure there will be many more to come!)
I’ve come from a background of more verbose languages and so I’m not sure how to achieve this without a loop which as I’ve seen is not very much at all in the nature of q programming.
One method I can think of using loops is to generate a random number between 0 and the predefined sum and then assign that to the first value of the list, and repeat each time whilst subtracting the number from the total.
e.g.
rand_list: {[length;total]
list:();
i:1;
while[i<length;
list,:temp:rand total+1;
total-:temp;
i+:1;
];
list,:total;
list
};
This however does not give a very good distribution, since it is much more likely to give the first values a much higher chance of being assigned a higher value. So to change this I thought to loop through all elements of the list while randomly generating either 1 or 0 and then add that to the atom in the list (while subtracting it from the total).
e.g.
rand_list_2: {[length;total]
i:0;
list:length#0;
while[total;
if[i=length;
i:0;
];
if[rand 2;
list[i]+:1;
total-:1;
];
i+:1;
];
list
};
This gives a much more even distribution, but is however massively inefficient due to the loop which I want to get away from entirely if possible?
reformulating the problem as a transformation of vectors removes the loop. the distribution of the values is definitely quite skewed (and not normal), but perhaps what you are looking for.
q)rl:{[l;t] deltas asc t,(l-1)?t+1}
q)rl[10] each 10#10
0 1 1 2 2 2 0 1 1 0
0 1 3 1 2 2 0 1 0 0
0 1 1 1 1 1 3 2 0 0
1 1 1 2 1 1 1 0 1 1
1 1 1 1 3 1 0 1 0 1
0 0 2 1 1 0 3 1 0 2
0 0 0 1 0 1 0 0 4 4
0 0 1 3 0 3 1 2 0 0
0 0 2 1 2 0 2 3 0 0
0 4 0 2 0 0 2 0 1 1
One way to generate an even distribution (probably too even) would be, for example:
value count each group 120?10
…to generate 10 “random” numbers which sum to 120 (they won’t really be random, each number will be very close to the mean/median). Its just generating 120 indices between 0 and 10 and then counting their frequency.
Above method is not so useful if you need more randomness, or more deviation from the mean. But it is neater and quicker
q)\ts a:value count each group 100000?1000
7 3145024
q)
q)\ts b:rand_list_2[1000;100000]
272 8576
q)count each (a;b)
1000 1000
q)sum each (a;b)
100000 100000
q)avg each (a;b)
100 100
q)med each (a;b)
99 100
Thank you for the quick response! This works great and is exactly the kind of form I was looking for, however I don’t understand how it works. I’m sorry to be a pain but if you could possibly explain that would be fantastic :)
Thank you for the reply. I love the simplicity behind your solution. Although slightly larger in terms of space usage, definitely orders of magnitude quicker than my attempts.
<o:p> </o:p>
s1:{[l;t]({r:first 1?x[0]+1;(x[0]-r;r)}[l;(t-r;r:first 1?t+1)])[;1]}<o:p></o:p>
<o:p> </o:p>
s1[11;10]<o:p></o:p>
<o:p> </o:p>
I need to add that nick solution might wont work if length is not event. Even if Nicks solution works its distribution is not what u will expected.<o:p></o:p>
<o:p> </o:p>
About this perhaps later.<o:p></o:p>
<o:p> </o:p>
Kim<o:p></o:p>
<o:p> </o:p>
Von: personal-kdbplus@googlegroups.com [mailto:personal-kdbplus@googlegroups.com] Im Auftrag von MykonBlu
Gesendet: Donnerstag, 21. Mai 2015 00:37
An: personal-kdbplus@googlegroups.com
Betreff: Re: [personal kdb+] Random list of set length which sums to predefined amount<o:p></o:p>
<o:p> </o:p>
Thank you for the reply. I love the simplicity behind your solution. Although slightly larger in terms of space usage, definitely orders of magnitude quicker than my attempts.
if you need the numbers to sum to t, then create a list of ascending numbers ending in t and then compute the differences.
random cumulative sums:
q)t:100
q)show x:t,9?t+1
100 66 35 17 24 47 98 27 37 77
sort the list:
q)asc x
`s#17 24 27 35 37 47 66 77 98 100
and compute differences:
q)deltas asc x
17 7 3 8 2 10 19 11 21 2
?you are now guaranteed to have a list of numbers summing to t without iterating.?