<jupyter_start><jupyter_text>Does modulus of sum of a uniform distribution also produce a uniform distribution? I have been working on an [Ethereum lottery](https://github.com/strange-labs-uk/ethereum-lottery) Dapp with folks attending a [meetup](https://www.meetup.com/Hacking-on-Ethereums-Solidity/) hosted by Strange Labs (Gloucester Road, Bristol) and we faced a problem where we realised that Solidity does not have a built in random number generator and therefore picking a winner turned out to be more difficult that anyone imagined.In order to make the system truly trustless, one of the ideas involved each participant generating their own random number and submitting it to the contract when they buy their lottery tickets. However I was concerned that taking a sum of of these numbers would result in a normal distribution allowing the early and late participants to have a built in disadvantage.To solve this, taking a modulus of the sum seemed like a way around the problem however I wanted to make sure that this was really the case. Therefore, [this notebook](https://github.com/brtknr/UniformModulus) is an exploration of whether the list of modulus really does produce a uniform distribution over lots of simulated scenarios.  Import relevant modules<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as ss<jupyter_output><empty_output><jupyter_text>Helper functions<jupyter_code>def test(num_of_vars_in_dist, random_num_max_val, num_of_random_nums, num_of_iterations):
    list_of_sums = list()
    list_of_indices = list()

    for i in range(num_of_iterations):
        list_of_randoms = np.random.randint(
            random_num_max_val,
            size=num_of_random_nums
        )
        
        sum_of_randoms = list_of_randoms.sum()
        list_of_sums.append(sum_of_randoms)
        index = sum_of_randoms % num_of_vars_in_dist
        list_of_indices.append(index)

    return dict(
        list_of_sums=list_of_sums,
        list_of_indices=list_of_indices,
    )

def hist(result, key, bins=100):
    plt.ion()
    plt.figure()
    plt.hist(result[key], bins=bins)
    plt.xlabel(key)
    plt.ylabel('density')<jupyter_output><empty_output><jupyter_text>Run the test<jupyter_code>result = test(
    num_of_vars_in_dist=100,
    random_num_max_val=1000,
    num_of_random_nums=100,
    num_of_iterations=10**5
    )<jupyter_output><empty_output><jupyter_text>Inspect the distribution of `list_of_sums`<jupyter_code>hist(result,'list_of_sums')
ss.kstest(result['list_of_sums'], 'norm', args=(), alternative='two-sided', mode='approx')<jupyter_output><empty_output><jupyter_text>It looks like the sum of random produce a two-sided distribution, the kind of behaviour we expect when we take a sum of all the numbers produced when we roll multiple dice where the mean of number of faces has the highest likelihood of manifesting.  Now lets look at the values of `var_dist`<jupyter_code>hist(result,'list_of_indices')
ss.kstest(result['list_of_indices'], 'uniform', args=(), alternative='two-sided', mode='approx')<jupyter_output><empty_output><jupyter_text>It looks like the list of indices are more or less uniformly distributed and the statistical test confirms our visual observation. Therefore, we can conclude that numbers generated by taking a modulus of sum of randomly generated numbers is a reliable pseudo-random number generator.<jupyter_code>!jupyter nbconvert --to markdown Notebook.ipynb<jupyter_output>[NbConvertApp] Converting notebook Notebook.ipynb to markdown
[NbConvertApp] Support files will be in Notebook_files/
[NbConvertApp] Making directory Notebook_files
[NbConvertApp] Making directory Notebook_files
[NbConvertApp] Writing 3778 bytes to Notebook.md
{'fraction_non_alphanumeric': 0.07009095773140717, 'fraction_numerical': 0.005350454788657036, 'mean_word_length': 5.210963455149502, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 24, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '87614', 'license': 'MIT', 'n_tokens_mistral': 1140, 'n_tokens_neox': 1097, 'n_words': 377, 'path': 'Notebook.ipynb', 'repo_name': 'brtknr/UniformModulus', 'size': '3738'}