3
$\begingroup$

I'm pretty new to Mathematica, and I'm mainly a programmer so I don't have a lot of knowledge about maths.

I want to generate a set of UNIQUE incremental numbers (series) according to the following rules:

  • From N1 to N2 INCLUSIVE
  • The count is exactly N e.g. I want 5000 numbers
  • Scales according to a function i.e. I want the distance between each number and the following number to increase sequentially for example I want it to scale such that the first 25% of the range contains 50% of the number (numbers close together) then next 25% contains 30% of the numbers, then 15% then 5%, that way as you move forward the numbers are stretched further apart.


Thanks,
SM

EDIT: The actual situation that makes me think I need this is:
I'm simulating a slot game where you can win an amount of money each spin, I get a long list of all the wins and their frequencies, now I want to "redistribute" or "group up" those range of wins into a smaller set, for example only 5000 numbers instead of having like 1M, I want to do that by basically generating a series as specified in my question and then "grouping" each of the real wins into the closest number in the generated set, so for example 0.9, 1 and 1.1 would be grouped into just 1 with frequency 3 (assuming that 1 is in the generated set of numbers)

EDIT: Now that my question has been answered, my next step should be:

  1. My new set of numbers has a weights list all initialised to 0 {0,0,0,...}
  2. Go through my original list and for each number I get its weight and add it to the weight of the nearest number in the new set.
    For example:
    Original: {1,2,3,4,5,...,10} (from 1 to 10)
    Weights: {1,1,1,1,..} (all have equal weight)
    New set: {1,10}
    so the weights for the new set would be calculated:
    {1 to 5} get "rounded" to 1 (closest number)
    {6 to 10} get "rounded" to 10 (closest number)
    New weights: {5,5}

I can do this in code easily but I'm sure there must be a way to do this in Mathematica? Should I post this as a new question? I don't want to spam

$\endgroup$
6
  • 1
    $\begingroup$ As a first start you could write one function as Piecewise spreading your values over your Range N. Just in case: Rule of Thumb - never use For- loops $\endgroup$
    – Sascha
    Nov 30, 2012 at 20:56
  • $\begingroup$ Thanks that's quite useful actually $\endgroup$ Nov 30, 2012 at 20:58
  • $\begingroup$ On second thought: What do you intend to use this list for? Since you said you are new to Mathematica, it could well be that there is a build-in function for what you intend to do. $\endgroup$
    – Sascha
    Nov 30, 2012 at 22:13
  • $\begingroup$ Thanks, I have edited my question $\endgroup$ Nov 30, 2012 at 23:09
  • $\begingroup$ I am a bit occupied at the moment, but my first guess for solving your problem would be GatherBy or BinCount, or BinList - depending on what exactly your output should look like. $\endgroup$
    – Sascha
    Nov 30, 2012 at 23:38

2 Answers 2

5
$\begingroup$

This might put you on track. Say your range is 1 to 50000. We can weight these in quartiles so that the first ones have 50% chance of being selected, 30% for the ones in the next quartile, etc. Then form a random sample without duplication using weighted RandomSample. Last sort so the result is ascending. Below is the code for this.

n = 50000;
rng = Range[n];
wts = Join[ConstantArray[1, n/4], ConstantArray[6/10, n/4], 
   ConstantArray[3/10, n/4], ConstantArray[1/10, n/4]];

SeedRandom[1111];
sample = Sort[RandomSample[wts -> rng, 5000]];

To check the quality I'll bin into quartiles, then find the length of each bin.

lens = Map[Length, BinLists[sample, n/4]]

(* Out[25]= {2474, 1505, 773, 248} *)

Let's get the respective fractions.

N[lens/5000]

(* Out[26]= {0.4948, 0.301, 0.1546, 0.0496} *)
$\endgroup$
2
  • $\begingroup$ very interesting answer, it seems to solve my problem I need to do some checks to be sure though, cheers for that $\endgroup$ Dec 1, 2012 at 0:09
  • $\begingroup$ yup that's what I needed. In my case I already have the weight for each number in the original list. $\endgroup$ Dec 1, 2012 at 20:15
3
$\begingroup$

Here is approach that doesn't rely on random numbers but instead generates a completely deterministic sequence of numbers that tracks the desired distribution optimally.

The distribution given in the example can be written like this, assuming all numbers are in percent:

ρ[x_] := Piecewise[{
           {50, 0 <= x < 25}, 
           {30, 25 <= x < 50}, 
           {15, 50 <= x < 75}, 
           {5, 75 < x <= 100}
         }]
Plot[ρ[x], {x, 0, 100}, Exclusions -> None, 
      AxesLabel -> {x, ρ}, BaseStyle -> Larger]

rho

Now I generate the desired numbers in a list called spacedOut, with a length of 5000 entries:

numberOfValues = 5000;
max = Integrate[ρ[x1], {x1, 0, 100}];
dx = max/numberOfValues;

spacedOut = 
  Most@Rest@NestWhileList[(# + dx/N[ρ[#]]) &, 0, (# <= 100) &];

ListPlot[spacedOut]

spaced

To check that we got the desired result, look at the number of values and their percent distribution:

Length[spacedOut] == numberOfValues
(* ==> True *)

100 BinCounts[spacedOut, {0, 100, 25}]/Length[spacedOut] // N
(* ==> {49.98, 30.02, 15., 5.} *)

The last line shows that the distribution is spot on, without having to rely on the law of large numbers in a random process.

Basically what I did in the NestWhileList was to construct the inverse of the integrated distribution in a discretized form. Or in other words, I used the relation $\Delta y = \rho(x) \Delta x$ where $x$ is in the interval $[0,100]$ and let $y$ increase in constant steps to calculate successive values of the corresponding $x$. The total number of steps is predetermined by calculating the integral of $\rho$ over the whole interval $[0,100]$ and adjusting the step width dx accordingly.

Therefore, the values in spacedOut will all lie between $0$ and $100$, but with a varying spacing to create the correct distribution.

The final step is to uniformly rescale and shift all the numbers in spacedOut so that its minimum and maximum agree with the minimum and maximum values n1 and n2 that you want:

n1 = 17; n2 = 511;
final = n1 + spacedOut*(n2 - n1)/100

This approach doesn't require a piecewise constant distribution. The function $\rho(x)$ can be an arbitrary distribution.

$\endgroup$
1
  • $\begingroup$ ha, I learned some new things by trying to understand your answer, still don't fully understand it to be honest (the Δy=ρ(x)Δx par), but thanks that was quite useful $\endgroup$ Dec 1, 2012 at 20:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.