Non-repetitive random seek in a range Algorithm

后端 未结 7 1646
终归单人心
终归单人心 2021-01-24 05:36

I\'m looking for an efficient algorithm that produces random values within a range, without repetitions.

In pseudo-code: (in class Rand)

  Rand(long from         


        
7条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-24 06:13

    @rossum said:

    A cypher is a one-to-one mapping, otherwise it couldn't be decrypted. Hence, any block cypher will map the numbers 0, 1, 2, 3, 4, 5, ... to different n-bit numbers, where n is the cypher's block size in bits.

    So even xor encryption or bitwise reverse would do for some purposes.

    And here is a php function using xor and bitwise reverse as a simple 1-to-1 encryption.

    It is a pseudo random number generator with guaranteed fill in of all values and no identical values. You supply n:0..63 and it provides a random 0..63.

    It only accepts 2^i ranges, like 0..63, 0..127 etc.

    Is not cryptographically safe etc, just random.

    Such a function is extremely suitable for garbage collection routines, as it will not attempt to clean the same area twice, even though doing things randomly.

     function math_random_filled($n,$bits,$seed)
     {
       //bits: examples: 6=0..63, 8=0..255, 10: 0..1023
       //n: 0<= n <2^$bits
       //seed: any string or number
    
       //generate xor: crc32 + bit mask
       $xor= crc32($seed.'internalseed') & ~(-1<<$bits);
       //xor
       $r=intval($n)^$xor;
       //bitwise reverse
       $r=bindec(strrev(str_pad(decbin($r),$bits,'0',STR_PAD_LEFT)));
       return $r;
     }
    
     //demonstration
     $bits=6;
     $min=0;
     $max=pow(2,$bits)-1;
     $count=$max-$min+1;
     for($n=0;$n<=$max;$n++)
     {
       $r=math_random_filled($n,$bits,$seed='someseed');
       echo " $r ";
       $ar[$r]=1;
     }
    
    
     $set=0;
     for($n=0;$n<=$max;$n++)
       if(isset($ar[$n])) $set++;
    
    
     echo "\n"."bits: $bits,  count: $count, set: ". $set."\n\n";
    

    example output:

     37  5  53  21  45  13  61  29  33  1  49  17  41  9  57  25  39  7  55  23  47  15  63  31  35  3  51  19  43  11  59  27  36  4  52  20  44  12  60  28  32  0  48  16  40  8  56  24  38  6  54  22  46  14  62  30  34  2  50  18  42  10  58  26 
    
    bits: 6,  count: 64, set: 64
    

    you can test the code here in php sandbox

    And here is another one, but accepts any range, not only powers of 2.

    function math_random_filled_arithmetical($n,$max,$seed)
        {
        /*
        - produces 0..max, repeatable, unique, one-to-one mapped random numbers
        - uses arithmetic operations to imitate randomness
        - $n: 0<=$n=<$max
        - $max: any integer, not only power of two
        - $seed: any string or number
        */
        $n=intval($n);
        $max=intval($max);
        $opt1=$n;
        $opt2=$max-$n;
        $n2=min($opt1,$opt2);
        $reverseit=crc32($seed.'internalseed'.$n2)&1;
        if($opt1>$opt2) $reverseit=!$reverseit;
        $max2=floor(intval($max-1)/2);
        //echo "n:$n, max:$max,n2:$n2,max2:$max2,reverseit:$reverseit\n";
        if($max>=3)
        if($opt1!=$opt2)
            $n2=math_random_filled_arithmetical($n2,$max2,$seed.'*');
        $res=$reverseit? $max-$n2:$n2;
        $res=intval(fmod($res+(crc32($seed)&(1<<30)),$max+1));
        //echo "n:$n, max:$max, res:$res\n";
        return $res;
        }
    
    
    //demonstration
    $max=41;//-- test a max value 
    for($n=0;$n<=$max;$n++)
        {
        $r=math_random_filled_arithmetical($n,$max,$seed='someseed');
        $ar[$r]=1;
        echo " $r ";
        }
    $filled=0;
    for($n=0;$n<=$max;$n++)
        if(isset($ar[$n])) $filled++;
    echo "\n count: ".($max+1).", filled: ". $filled."\n";
    

    example output:

     20  19  18  17  33  32  37  36  14  13  31  34  35  26  16  11  12  3  39  40  0  41  1  2  38  29  30  25  15  6  7  10  28  27  5  4  9  8  24  23  22  21 
     count: 42, filled: 42
    

    related php sandbox is here

提交回复
热议问题