[HN Gopher] random(random(random(random()))) ___________________________________________________________________ random(random(random(random()))) Author : Alifatisk Score : 109 points Date : 2023-05-15 19:23 UTC (3 hours ago) (HTM) web link (openprocessing.org) (TXT) w3m dump (openprocessing.org) | davesque wrote: | Since `random(x)` returns a random number in [0,x), it will | always output a number that is less than `x` (the initial value | of x is 1 in this case). Therefore, the outcome where repeated | applications of `random(...)` to its own result has little | shrinking effect is vanishingly small. It's the result of these | repeated applications that is being subtracted from the length of | the unit vector that is used to position each pixel. So the | pixels naturally accumulate around that unit length. No broken | crypto here. | | It's sort of like asking, "What would happen if I multiply a | bunch of numbers together that are guaranteed to be less than 1 | (and even much less than 1 much of the time)?" | gabereiser wrote: | Exactly, like Perlin noise, the output is not indicative of the | function. Works as intended. The beauty is in the fact that | when using pseudorandom number generation and the right output | function, you can simulate a lot of things. Star systems, | planets, stock market games, aim handicap, and itano circus. | awegio wrote: | In more formal terms, since it is a uniform distribution you | can write random(x) = random()*x | | then you can easily see that | random(random(random())) = random()*random()*random() | | The resulting distribution is not equal to random()^3, because | e.g. the probability that all 3 random calls give you a number | <0.5 is only 0.125. | muti wrote: | Fork wrapping the random radius in sqrt() so the first disk is | uniformly distributed | | https://openprocessing.org/sketch/1929418 | chkas wrote: | Shameless copied: | https://easylang.dev/ide/#run=textsize%203%0Afor%20i%20%3D%2... | jtsiskin wrote: | This would be way clearer as a Cartesian plot with the output of | random as X, and sample number as Y. | dathinab wrote: | It's a funny one. | | On the first look it looks that random is broken then you | realize, no random(n) is a random value between [0;n) so it's | behaving exactly right. | | It also shows really nice how "naively" choosing random points in | a non rectangular shape can easily lead to a non uniform | distribution. | SideQuark wrote: | Lots of math all over the place computing the expected value :) | | So, here's a derivation: if rand(x) returns uniformly a real in | [0,x) or equivalently for expected value, in [0,x], then here are | the first few iteration expected values from which you can see | the general answer. 1 term E(r) = (1/1) * | Integral_0^1 x_0 dx_0 = (x_0^2/2) from 0 to 1 = 1/2 | | 2 terms E(r(r)) = (1/1) * Int_0^1 (1/x_0) | Int_0^x_0 x_1 dx_1 dx_0 = 1/4 | | 3 terms: E(r(r(r))) = (1/1) * Int_0^1 (1/x_0) | Int_0^x_0 (1/x_1) Int_0^x_1 x_2 dx_2 dx_1 dx_0 = 1/8 | | And so on. The nth term is exactly (1/2)^n | tbalsam wrote: | What language? What are the parameters for the random function | here (seed? upper range value in a uniform distribution? stddev | for a random distribution?) Why? What is this demonstrating? | | I can grok a lot of the dimensional effects of randomness but | without these things specified, the picture is rather meaningless | to me. | maxbond wrote: | 1. The language is Processing | | 2. The argument is the high end of the range (0 to n | [https://processing.org/reference/random_.html]) | | 3. I dunno why, it seems like it's just cool. It seems to | demonstrate that random(random(...)) collapses to 0, which is | exactly what I expected but it's pretty cool. | justinpombrio wrote: | Q4: What is it plotting? `random()` returns a float, right? | Is it plotting... r*cis(theta), where r is the random | invocation and theta varies uniformly across the circle, | normalized so that all the circles are the same size (where | cis(theta) = (cos(theta), sin(theta))? | maxbond wrote: | Nah, it's plotting polar vectors, and it uniformly picks an | angle. The random(...) is just the magnitude of the vector. | justinpombrio wrote: | FYI, r*cis(theta) _is_ a polar vector. So it's the thing | I said, but with the angle chosen randomly. | [deleted] | tbalsam wrote: | I'm a little confused then what the value of this | demonstration is. | | The expectation marches by half the distance towards the | outer edge each time, so it becomes a soap bubble at a rate | of 1/2^n | | Which is nice and sorta cool but I'm not quite sure what the | "whoa" factor here is. It's like one of those zip cinch tops | for those bags. Sorta cool how it works on observation, | really cool in detail, but also sorta...meh? At the same | time? | | Maybe I'm a bit jaded here? Admittedly, a continuous | animation of this would be an improvement on the cool factor | to me, personally. | maxbond wrote: | Well, all I can tell you is that I'm having a good time | discussing the nitty gritty of it in the comments here, and | it made the gears of my mind turn in a pleasant way. It | didn't make me go, "wow," but it did make me go, "hmm...." | tbalsam wrote: | Gotcha, yeah, that makes sense to me now. | | Maybe the real article was the HN comments section that | we made along the way.... | walterbell wrote: | _> Maybe the real article was the HN comments section | that we made along the way...._ | | Nominating this comment for | https://news.ycombinator.com/highlights | [deleted] | sobellian wrote: | I think the sequence tends toward the edge at exp(-n), not | 2^-n. The distance from the edge is a product of n I.I.D. | variables, so the logarithm is a sum of n I.I.D. variables, | and the central limit theorem gives us the result. | | You can confirm in a python terminal (or anything else) | that the product of n `random()`s decreases more rapidly | than 2^-n. | | So maybe there's some value in it after all :) | tbalsam wrote: | How do you mean? | | I'm going based on the propagation of expectations | through the system. | | The expectation of a uniform distribution is half the | bound. Unless there's some math shenanigans going on, I | believe that the expected expected value of a | distribution drawn from distributions should be 1/2 * 1/2 | * 1 in this case. | | Of course it's not a bound but right now I'm having a | hard time determining otherwise. | | Is there a mathematical motivation for e^-n here? That | seems an odd choice since the uniform distribution is 0-1 | bounded generally. However I could understand if it | emerges in a few conditions. | sobellian wrote: | e shows up because we're doing an iterated product (of | random variables, but still). If you look at the central | limit theorem, sum(log(rand())) tends to N * | E[log(rand())] for large N. Well, what's E[log(rand())]? | -1! | | Like I said, it's fairly easy to test this in a python | terminal. I encourage anyone who doubts me to try it :) | tbalsam wrote: | Alright, I tried it in the terminal and the numbers | confirmed my earlier math. .5->.25->.125 mean as | N->infinity. I chained the python uniform function like | the above code in the originally linked post does and | averaged the results. | | The snark to me and the other commenters is a bit | unnecessary in either case. I'm not really sure where | you're getting a natural log or an iterated product of | random variables in this instance. | | Could you perhaps show where you're transforming | uniform(uniform(uniform(uniform(0, 1)))) into the math | you're showing above? I'm trying to follow along here but | am having difficulty connecting your line of reasoning to | the problem at hand. | sobellian wrote: | Not trying to be snarky by any means. I'm sorry if you | interpreted it that way. | | I don't think the difference will show up for small N. | This is an asymptotes thing. Try it for N = 100, that's | what I did. For example: >>> | np.product(np.random.random(100)) | 5.469939152265385e-43 >>> 1 / np.e**100 | 3.7200759760208555e-44 >>> 1 / 2**100 | 7.888609052210118e-31 | | The underlying thing here is that random(random()) in | this case is the same as random() * random(). So | random(random(random(...))) is the same as random() * | random() * random() and then the analysis goes on. And | sure, random() * random() has a mean close to 1/4. But | the dynamics change as N becomes large. | | Edit - and just in case you doubt whether random() * | random() * ... is a valid way of doing this, I also just | checked the laborious way of doing it: | >>> def foo(n): ... result = 1.0 ... for | _ in range(n): ... result = | np.random.uniform(0, result) ... return result | ... >>> foo(100) 1.4267531652344414e-46 | >>> foo(100) 7.852496730908136e-49 >>> | foo(100) 1.3216070221780724e-41 | tbalsam wrote: | > I'm sorry if you interpreted it that way. | | is probably a good marker that it is time for me take my | bow in this conversation, however, for an alternative | approach I recommend SideQuark's comment on iterative | random variables vs chains of multiplied random | variables, which have different characteristics when | defined as a sequence. | sobellian wrote: | I checked his comment, I think he's incorrect on the | approaches being different. Using my function `foo` that | does the iterative approach, we can compare the | distributions and they're fairly identical. | >>> np.mean([foo(100) for _ in range(100000)]) | 3.258425093913613e-33 >>> | np.mean([np.product(np.random.random(100)) for _ in | range(100000)]) 8.814732867008917e-33 | | (There's quite a bit of variance on a log scale, so 3 vs | 8 is not a huge difference. I re-ran this and got varying | numbers with both approaches. But the iterated code is | very slow...) | | Note that the mean is actually quite a bit closer to | 2^-100 even though the vast majority of numbers from | either distribution fall below 2^-100. Even so, the mean | for both is approximately a factor of 100 less than | 2^-100. Suspicious! Although I think we've both burned | enough time on this. | SideQuark wrote: | >The distance from the edge is a product of n I.I.D. | variables | | It is not a product of random variables; it is an | iterated random variable. The output of one influences | those higher in the chain. Redo your python code with | rand(rand(rand...))) not rand() * rand() * rand()... | | The expectation of composition of functions is not the | composition of expectations, so there is some work to do. | | For uniform over [0,1] for the innermost item, it becomes | an iterated integral, with value (1/2)^n. | sobellian wrote: | I did redo it, and the distributions are identical. It's | just a very heavily skewed distribution, so the expected | value is not very intuitive. I still think even E[x] | decreases faster than 2^-n though. See my other comments. | Nevermark wrote: | Except that your logarithms can have any consistent base, | including 2. | | So I don't think using a logarithm to introduce a special | dependence on e is warranted in this case. | sobellian wrote: | Well the expected value of E[log2(rand())] is not the | same as E[log(rand())] and therein lies the difference. | See my sibling comment. | | Like I said, you can check this in a python terminal. | nimih wrote: | Speaking personally, a webpage with nothing beyond a cool | or interesting sequence of images is already in the 90th | percentile of good HN posts. | [deleted] | [deleted] | jlhawn wrote: | This is a drawing app using P5.js | | Click the "</>" at the top to read the code. | | There's a popular "Let's Code" YouTube channel called The | Coding Train that uses it. It's quite acccessible. | dist-epoch wrote: | The documentation suggests random() without arguments is not | supported random(high) random(low, | high) | | https://processing.org/reference/random_.html | TaylorAlexander wrote: | Oooh it's a high value, I thought it was a seed and that this | was demonstrating a flaw in the algorithm. | tbalsam wrote: | Same, at first at least. | romwell wrote: | It's almost criminal to post things like that without any | annotations. | | The first picture plots points where radius and angle (in polar | coordinates) are uniformly distributed. Of course, this is _not_ | uniform in the disc. | | I don't have the time right now, but if I do later, I'll type up | the math behind what a uniform distribution in the disc looks | like, and what's going on in other pictures. | [deleted] | klyrs wrote: | the bigger crime is that this is plotting | 1-Random(Random(Random(Random()))) | | and not Random(Random(Random(Random()))) | fsckboy wrote: | just in terms of pure randomness (which is not what this is | testing), random(random()) doesn't make more sense than random(). | If your random number generator is good, one is enough. If it's | not good, multiple times is not going to help. | | I'm putting this in the past tense, it randumb enough times for | me. | the_af wrote: | The argument to random() in this case is the upper bound, so | `random()` and `random(random())` are different. | | See: https://processing.org/reference/random_.html | alhirzel wrote: | Yep, just about what one would expect from randomly cutting the | top of your uniform distribution off multiple times! | | What's interesting is the transforms used to sample in some new | space from e.g. uniform random inputs. For instance, disks [1] or | hemispheres [2] or an arbitrary density function [3]. Common | stuff in light transport simulation, and easy to get wrong. | | [1]: | https://stats.stackexchange.com/questions/481543/generating-... | | [2]: https://alexanderameye.github.io/notes/sampling-the- | hemisphe... | | [3]: https://en.wikipedia.org/wiki/Inverse_transform_sampling | maxbond wrote: | If you're wondering why the dots approach the circumference and | not the center, the length of the vector is `diameter * (1 - | random(random(...)))` | | It's interesting that the second circle is a pretty uniform | distribution. `1 - random(random())` must be approximately | `sqrt(random())`, iirc that's how you correct for the error | demonstrated in the first circle | (https://youtube.com/watch?v=4y_nmpv-9lI). | bonzini wrote: | ... That means each point is at a distance of sqrt(f(x)) from | the circumference. And nesting random() n times is similar to | random()^n, which explains why the first plot has them amassed | in the center and the second has a more uniform distribution. | | Why are they similar? For a rough idea, random() is a random | number between 0 and 1, which on average is 0.5; | random(random()) is a random number between 0 and on average | 0.5, which on average is 0.25; and so on. | ozfive wrote: | Oh man if my math teachers taught math with application, I | would have been so much better off. It's so much easier to | understand all of this after writing code for a while. | avodonosov wrote: | A better title would be "1 - Random(Random(Random(Random())))", | than it's more understandable why the points lean to the | circumference. | [deleted] | contravariant wrote: | The cumulative distribution of random(random(...)) repeated k | times is 1-(1-x)^k, so 1 - random(random()) should have a | cumulative distribution of x^(2). | | I think that should even make it exactly uniform, but somehow | it doesn't look like it. I may have missed a bit somewhere. | maxbond wrote: | Perhaps it looks nonuniform because we're not using literal | points (our dots have nonzero area), so when they get too | close they blend together and make the distribution look | denser than it is in that area | contravariant wrote: | Could be, but it is also possible I'm missing something | somewhere, especially because I can't explain why | min(random(), random()) looks different from | random(random()) or random()*random(). | penteract wrote: | An attempt to explain the difference without just | calculating the things: | | random(random()) has an identical distribution to | random()*random() (it may even behave identically for a | given rng state), although this is different to | Math.pow(random(),2) since in that case there's 100% | correlation between both parts which makes the expected | value product bigger. | | random(random()) is also distributed equivalently to | x=random() y=random() while(y>=x) | y=random() return min(x,y) | | (the last line could also read 'return y') Comparing that | to min(random(),random()), we can see that if the second | call to random is smaller than the first, they will | return the same result; otherwise, the program equivalent | to random(random()) will return a smaller value, | therefore the expected value of random(random()) must be | lower that that of min(random(),random()). | awegio wrote: | According to the internet what you wrote is the | distribution of min(random(), random()), but the | distribution of the product is different. | | Product: | https://math.stackexchange.com/questions/659254/product- | dist... | | Minimum: | https://stats.stackexchange.com/questions/220/how-is-the- | min... | | I can't explain it either, but I also don't think there | is a reason they should look the same. | paulddraper wrote: | Sorry to be blunt, but what is of interest here? | | Aesthetics? | | P.S. In case anyone is confused, the arg to random is the upper | limit (not the seed), and r = diameter * (1 - value) | kibwen wrote: | I find it cool that this website lets you upload code for | visualizations, and then lets any user modify that code and re- | run it. It's like if Gist let you render images from code, | rather than from just SVG. | cheeze wrote: | Yeah, this was a very fun illustrative view of why this is a | bad idea. | edrxty wrote: | Yeah the code under the </> tab is very important, on first | blush it looks like random isn't a uniform distribution for | some reason. It's just because they plotted it with polar | coordinates. It would be much clearer what's actually going on | here if an XY plot was used. | paulddraper wrote: | No, it's because random(random(random())) is iterating the | upper limit, not the seed. | qingcharles wrote: | This is the answer. I think the author is confused, as most | platforms do have the seed as the lone parameter to their | Random function. | maxbond wrote: | It is uniform I'm pretty sure, that's what plotting a polar | coordinates look like when you use a uniform distribution for | the length. | edrxty wrote: | Isn't that what I said? | maxbond wrote: | Oh I misunderstood, my bad. Upon rereading I see what you | meant. | andybak wrote: | Most things are interesting. | utunga wrote: | This would've been a lot less confusing if they'd used | | let radius = d * random(random(random())) | | instead of | | let radius = d * (1 - random(random(random()))) | | But, if so, probably so much less confusing that folks wouldn't | be talking about it here, to be honest. | [deleted] | rjbwork wrote: | Just whipped this up in Linqpad real quick to see how quickly | random(random(...)) tends to converge to 0. | | Fun lil script. | | https://media.discordapp.net/attachments/519790546601115649/... | | EDIT: Realized I cut off the full script. First two lines are: | var ran = new Random(); var counts = new | Dictionary<int,int>(); | thriftwy wrote: | I like using Math.min(nextInt(), nextInt()) for non-uniformly | distributed random data. | [deleted] | gnramires wrote: | Tips: The source code can be seen by clicking on '</>'; The | random() function takes an upper limit of a desired uniform | distribution (from 0 to x), this shows the effect of the | distribution of those iterated random functions. | | I think this isn't so intuitive because the polar coordinates | already imply greater density near center (which could be | corrected with a suitable map). | em-bee wrote: | right, if the points are distributed in a square then the | distribution looks more like what one would expect: | | (i am to lazy to register to make a fork, here is the code | change): | | replace: let a = random(Math.PI*2); let r | = d * (1 - random()); point(cos(a)*r, sin(a)*r); | | with: let x = random(d); let y = d * (1 - | random()); point(x, y); | | in every block | | you could even simplify it to: let x = | random(d); let y = random(d); point(x, y); | | and for more fun apply the nested random() call to both axis: | let x = random(random(d)); let y = random(random(d)); | point(x, y); | acjohnson55 wrote: | The reason it's counterintuitive is because the randomly chosen | distance variable is 1 minus the nested randoms. Each random | call becomes the upper bound of future random calls, so the | more times you nest it, the closer to zero you're likely to be. | Subtracting that from one puts you close to the unit circle. ___________________________________________________________________ (page generated 2023-05-15 23:00 UTC)