{"id":19412784,"url":"https://github.com/techshot25/random-number-generators","last_synced_at":"2025-08-02T06:06:04.437Z","repository":{"id":213791578,"uuid":"223430504","full_name":"techshot25/Random-Number-Generators","owner":"techshot25","description":"How to generate pseudo random numbers","archived":false,"fork":false,"pushed_at":"2019-11-23T03:54:41.000Z","size":55,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-25T02:44:07.804Z","etag":null,"topics":["pseudo-random-generator","random-distributions","random-number-generators","sampling-methods"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/techshot25.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-11-22T15:20:37.000Z","updated_at":"2021-08-26T08:09:39.000Z","dependencies_parsed_at":"2023-12-23T05:35:15.312Z","dependency_job_id":null,"html_url":"https://github.com/techshot25/Random-Number-Generators","commit_stats":null,"previous_names":["techshot25/random-number-generators"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/techshot25/Random-Number-Generators","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techshot25%2FRandom-Number-Generators","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techshot25%2FRandom-Number-Generators/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techshot25%2FRandom-Number-Generators/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techshot25%2FRandom-Number-Generators/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/techshot25","download_url":"https://codeload.github.com/techshot25/Random-Number-Generators/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/techshot25%2FRandom-Number-Generators/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268339638,"owners_count":24234553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pseudo-random-generator","random-distributions","random-number-generators","sampling-methods"],"created_at":"2024-11-10T12:28:13.562Z","updated_at":"2025-08-02T06:06:04.387Z","avatar_url":"https://github.com/techshot25.png","language":"Jupyter Notebook","readme":"# How to generate pseudo random numbers\n## By Ali Shannon\n\nMost natural phenomenon such as weather are not easily predictable since they cannot be reproduced over and over again for testing via the scientific method, there are also certain data that can be observed long enough to estimate a distribution from, such as the rate at which droplets fall from a leaky faucet. We can observe the latter for long enough time to make a kernel density estimate of what the distribution may be like.\n\nComputers are deterministic and generally do not excel in generating truly random numbers. But today many algorithms generate pseudo-random numbers and rely on certain mathematical tricks to simulate random numbers that behave much like things we observe in real life. \n\nCredit to [Kelsey Houston-Edwards](https://youtu.be/C82JyCmtKWg) for teaching me the foundations of pseudo-random number generators and how they work.\n\n---\n\nHere I attempt to explain two of the most common ways to generate pseudo-random using python. But first let us explain the concept of a _seed_. A seed is the initial state or the starting point for our random number generators, this makes the set of random numbers generated by the algorithm reproducible, which means that if you run this code, you will get the same \"random\" numbers that I get. Other ways to generate seed such as using the digits from the current Unix time are harder to reproduce so I will keep it simple here.\n\n\n    The goal is to produce a uniform distribution, we can produce other distributions from that.\n\n\n### 1) Middle Square Algorithm\n\n- This algorithm relies on the squares of some initial number *\"seed\"* and using some number of digits (typically 4) in the middle to generate more numbers. \n\n- Here I am demonstrating the \"Middle-Four\" which generates a four digit number by taking the middle four digits some large number.\n\n\n```python\n# Dependencies: matplotlob\n\nfrom matplotlib import pyplot as plt\nfrom math import log\n```\n\nThe first function, `middle_four`, returns the four middle indices of any given number.\n\nThe second, `middle_square`, is a generator function that keeps making numbers until a repetition is observed. It is described in the following formula:\n\n\\begin{equation*}\nX_{n+1} = \\text{middle four digits of } X_{n} ^ {2}\n\\end{equation*}\n\n\n```python\ndef middle_four(number):\n    count = 0\n    while number \u003e 0:\n        number //= 10\n        count += 1\n    count = max(4, count)\n    return slice(count // 2 - 2, count // 2 + 2)\n\ndef middle_square(seed=1738):\n    history = set()\n    random_num = seed\n    while random_num not in history:\n        history.add(random_num)\n        random_num = str(random_num ** 2).zfill(4)[middle_four(random_num)]\n        random_num = int(random_num)\n        yield random_num\n```\n\n\n```python\nrng_1 = [x for x in middle_square(1738)]\nrng_2 = [x for x in middle_square(7332)]\n\nfig, ax = plt.subplots(1, 2, figsize=(15, 4))\nax[0].hist(rng_1, bins=50)\nax[0].set_title('Seed = 1738')\nax[0].set_ylabel('Count')\nax[1].hist(rng_2, bins=50)\nax[1].set_title('Seed = 7332')\nax[1].set_ylabel('Count')\nplt.show()\n```\n\n\n![png](output_4_0.png)\n\n\nThe numbers do not seem to converge to a uniform distribution. And the periods are not large enough.\n\n\n```python\nprint('First RNG period', len(rng_1))\nprint('Second RNG period', len(rng_2))\n```\n\n    First RNG period 89\n    Second RNG period 279\n    \n\n### 2) Linear Congruential Generator\n\nThis is a more robust random number generator and it works by taking the \"seed\" multiplying it by sum number `a`, adding some other number `c`, and finally taking modulus or remainder against `m`. The formula works like this:\n\n$$\nX_{n+1} = \\left ( a \\cdot X_{n} + c \\right ) \\mod{m}\n$$\n\nThe great thing here is that the modulus defines the upper limit and this generates far more numbers than the previous algorithm. We can simply divide it by the largest number (m - 1) to normalize it to the interval of [0, 1]\n\n\n```python\ndef lcg(seed=4321, m=7829, a=378, c=2310):\n    running_val = seed\n    nums = set()\n    while running_val not in nums:\n        nums.add(running_val)\n        running_val = (running_val * a + c) % m\n        yield running_val / (m - 1)\n```\n\n\n```python\nuniform_random_samples = [num for num in lcg()]\nplt.hist(uniform_random_samples, bins=50)\nplt.show()\n```\n\n\n![png](output_9_0.png)\n\n\nNow we have written an algorithm that generates a uniform random distribution $\\in [0, 1]$\n\n---\n\n### Inverse Transform Sampling\n\nThis trick relies on the inverse of the cumulative distribution function $\\text{CDF}^{-1}$ so it maps the numbers from the uniform output as the values that the CDF is associated with.\n\nFor simplicity's sake, let us take the following PDF which will represent a form of logistic distribution.\n\nThe given PDF is:\n\n$f(x) = \\frac{e^{-x}}{(1 + e^{-x})^2}$\n\nWhich makes the CDF:\n\n$F(x) = \\int{f\\; dx} = \\frac{1}{1 + e^{-x}}$\n\nAnd thus:\n\n$\\text{CDF}^{-1} = -\\ln{ \\left ( \\frac{1}{x} - 1 \\right )}$\n\n\n\n```python\ndef dist_samples(inverse_cdf=None, seed=4321, m=7829, a=378, c=2310):\n    running_val = seed\n    nums = set()\n    while running_val not in nums:\n        nums.add(running_val)\n        running_val = (running_val * a + c) % m\n        if callable(inverse_cdf):\n            yield inverse_cdf(running_val / (m - 1))\n        else:\n            yield running_val / (m - 1)\n```\n\n\n```python\ninverse_logistic = lambda x: -log(1 / x - 1) if (0 \u003c x \u003c 1) else 0\n\nlogistic_random_samples = [x for x in dist_samples(inverse_cdf=inverse_logistic)]\nplt.hist(logistic_random_samples, bins=50)\nplt.show()\n```\n\n\n![png](output_12_0.png)\n\n\nAnd thus, we have a random logistic distribution, which resembles a Gaussian distribution. The reason I avoided the Gaussian is because it involves the inverse error function since that is the CDF of the Gaussian PDF.\n\n\n```python\ninverse_exp = lambda x: -log(x) if (0 \u003c x \u003c 1) else 0\n\nexp_random_samples = [x for x in dist_samples(inverse_cdf=inverse_exp)]\nplt.hist(exp_random_samples, bins=50)\nplt.show()\n```\n\n\n![png](output_14_0.png)\n\n\nModifications to the lambda function modifies the shape of the ending distribution.\n\nIf you really want to see a Gaussian distribution, you can import `erfinv` from `scipy` library. And just for fun, you can also assign a mean of 2 and a variance of 3 to get the shape below.\n\n\n```python\nfrom scipy.special import erfinv\n\nmu, s = 2, 3\ninverse_gaussian = lambda x: mu + s * erfinv(2 * x - 1) if (0 \u003c x \u003c 1) else 0\ngaussian_random_samples = [x for x in dist_samples(inverse_cdf=inverse_gaussian)]\nplt.hist(gaussian_random_samples, bins=50)\nplt.show()\n```\n\n\n![png](output_16_0.png)\n\n\nTo wrap up, now we have a way to generate random numbers that obey a uniform distribution using the Linear Congruential Generator, and them we were able to use the Inverse Transform Sampling to get other distributions.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechshot25%2Frandom-number-generators","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechshot25%2Frandom-number-generators","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechshot25%2Frandom-number-generators/lists"}