Given a number, produce another random number that is the same every time and distinct from all other results

心已入冬 提交于 2019-11-27 09:46:46

This sounds like a non-repeating random number generator. There are several possible approaches to this.

As described in this article, we can generate them by selecting a prime number p and satisfies p % 4 = 3 that is large enough (greater than the maximum value in the output range) and generate them this way:

int randomNumberUnique(int range_len , int p , int x)
    if(x * 2 < p)
       return (x * x) % p
    else
       return p - (x * x) % p

This algorithm will cover all values in [0 , p) for an input in range [0 , p).

Here's an example in C#:

    private void DoIt()
    {
        const long m = 101;
        const long x = 387420489; // must be coprime to m

        var multInv = MultiplicativeInverse(x, m);

        var nums = new HashSet<long>();
        for (long i = 0; i < 100; ++i)
        {
            var encoded = i*x%m;
            var decoded = encoded*multInv%m;
            Console.WriteLine("{0} => {1} => {2}", i, encoded, decoded);
            if (!nums.Add(encoded))
            {
                Console.WriteLine("Duplicate");
            }
        }
    }

    private long MultiplicativeInverse(long x, long modulus)
    {
        return ExtendedEuclideanDivision(x, modulus).Item1%modulus;
    }

    private static Tuple<long, long> ExtendedEuclideanDivision(long a, long b)
    {
        if (a < 0)
        {
            var result = ExtendedEuclideanDivision(-a, b);
            return Tuple.Create(-result.Item1, result.Item2);
        }
        if (b < 0)
        {
            var result = ExtendedEuclideanDivision(a, -b);
            return Tuple.Create(result.Item1, -result.Item2);
        }
        if (b == 0)
        {
            return Tuple.Create(1L, 0L);
        }
        var q = a/b;
        var r = a%b;
        var rslt = ExtendedEuclideanDivision(b, r);
        var s = rslt.Item1;
        var t = rslt.Item2;
        return Tuple.Create(t, s - q*t);
    }

That generates numbers in the range 0-100, from input in the range 0-100. Each input results in a unique output.

It also shows how to reverse the process, using the multiplicative inverse.

You can extend the range by increasing the value of m. x must be coprime with m.

Code cribbed from Eric Lippert's article, A practical use of multiplicative inverses, and a few of the previous articles in that series.

You can not have completely unrelated (particularly if you want the reverse as well).

There is a concept of modulo inverse of a number, but this would work only if the range number is a prime, eg. 100 will not work, you would need 101 (a prime). This can provide you a pseudo random number if you want.

Here is the concept of modulo inverse:

If there are two numbers a and b, such that

(a * b) % p = 1

where p is any number, then

a and b are modular inverses of each other.

For this to be true, if we have to find the modular inverse of a wrt a number p, then a and p must be co-prime, ie. gcd(a,p) = 1

So, for all numbers in a range to have modular inverses, the range bound must be a prime number.

A few outputs for range bound 101 will be:

1 == 1
2 == 51
3 == 34
4 == 76
etc.

EDIT:

Hey...actually you know, you can use the combined approach of modulo inverse and the method as defined by @Paul. Since every pair will be unique and all numbers will be covered, your random number can be:

random(k) = randomUniqueNumber(ModuloInverse(k), p)      //this is Paul's function
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!