Nestharus
o-o
- Reaction score
- 84
Statistics
Why do numbers around 65000 have output between 4000 and 1.2 million?
Tests going over larger numbers in different scenarios to see if any patterns arose.
Requirements:
Demo
As the data set increases in value or decreases in value, the average increases/decreases in value. Because of this, lower values are weighted on one side and higher values are weighted on another side with medium values in the center. This is the reason why this fails the runs test with large numbers of data.
This behavior is to be expected and is actually desirable for this =).
Within a local area of 20 or 80, the data is very even as the data set isn't large enough to cause this effect.
So given a number, you can expect it to fall within a specific range. The larger the number, the bigger the range it can fall in. As save/load codes are very large, the effect that takes away from the randomness will take much longer to occur (I was dealing with a very small value of 65536), meaning that this will pretty much generate a random number given a number.
To give you an idea of the range for larger numbers, 65536 goes between around 4500 and 1.2 million.
Why do numbers around 65000 have output between 4000 and 1.2 million?
Going above 65536, the bits in a given number is 17.
When a number is shuffled in something like base 3, 1.5 bits are moved around for every movement. Every digit in base 3 is made up of 1.5 binary digits.
In base 3: 2
In base 2: 10
In base 5: 4
In base 2: 100
In base 7: 6
In base 2: 110
In base 11: A
In base 2: 1011
So these different fractions split apart and combine binary digits, thus giving the absolutely wild ranges that can be seen coming from Scrambler.
This makes reading a number w/o knowing the algorithm used to mix it up totally impossible.
Recall that this algorithm both includes the bases used in every shuffle as well as the hash of the player's name + a string value.
Tests going over larger numbers in different scenarios to see if any patterns arose.
Code:
Base 64= <23456789abcdefgh>+kmn=pqrs^uvwxyzABCDEFGHKLMNOPQRSTUVWXYZ@#?&%$
Base 63= <23456789abcdefgh>+kmn=pqrs^uvwxyzABCDEFGHKLMNOPQRSTUVWXYZ@#?&%
2^50= 1125899906842624
Prime= 2736653538294121
64 2^50 64 Prime 63 Prime
5<<<<<<<< aOgBf2xDH c2P%VhA2G
78BWPx4ak 8QvfC6w0= 9SkYRdE=Q
9ZW=HQkA? 87cb<Fe#X 947HQ^wHc
bzv3sagrd DL6T+LaNk KO6GE%hh+
7<ckQ5NvA ghDcDxVC2 >kq#u<FeQ
aXX>gPEf3 hXfmUS^fW k9<5%8%YS
7VfZ^+pp< eb9qPFDMr f@>s%r#d@
cy+exR<Vd dUgsSYYsE fzUFx8&Zh
b9dq=wZm9 7WXeu@#=h 8R<EbfxW#
cMWgrSMCa qMfGu<>Uq u<qY%?dGW
6>cbzR$ZK cVCFmTk5Y esW=DDggD
6e5KvR=cc 6A=EM&3@E 7>H23Fe>S
ecx@xfZaN fSUkNB9E> hRb+#WKgs
9d<d<Rrkr d^=hvCZxU f73CZ3MTw
dZZ#YZGVY dAg#kdLTv feP2xDZ^=
6Q9bC?v43 h@frwy8KR kc=cMXxcn
gMsnqqYHO 6wKaaVf8> 7ez8KNup?
9qdE$qD?7 exY3&8YW+ gkA%#u5x0
9yb>PAnzB cFFg#ewOs ec>v7OzPs
d%sRLp<B2 +p&uf#rcu mV<NF3uEg
9cDXCXaQq gBZdwbb5m >G?^%^z%5
7hm=GHHfa 8UU4POXV+ 9Xc=@vaM+
h>@FQTn>Z v%98dEk8% z%K^=X5fX
h6&$Ugb&b z6fNH9%rn Dy&SO#m53
9a?f%h5?4 8da^GrN?D 9aQT0>^rb
bHAngfKHd 5KQCAykzL 6+Cqs8hwF
b%&AVyAd? >L%qWZ+AR m4YMpD?6L
9KuBGS#4= 7gSvhG736 86q33fYKL
+FxxpdyUv r4=v7?zqf usepKf7F^
aHxgyVUT+ yDPW<4bY= C?UhP>Z=a
5ey=qZ@ED 9UzLQ<V@e b<m5N<+5Y
>T8hF#svM bVR#p@aY< d+G6mrC%3
k9S$bT8&g cNwnSbS$G e>TNGAKg6
9g+<phf4U xUyFMrZFH C6VP?SB=N
+qYLsue9h C60^LBKGy G@ZqvCF6W
5UCc86Z+5 k>y<6y>q$ nWheVMbbm
aQT=RAm6G c%uUZF4BN eCQXspa+>
7sap#gBNf 9QkEp<WD9 a@EfeUq%0
dGzsYGqZ% >KmKc%S8< m34xnP4v%
dAmXak7S8 d3aVKvsSR eGZpXzAAm
7w3VvCTsF fn7536Vzm hg&A^8KEy
6>=UEw@WP hkwFcMg@T +x6Hspq=x
>Ym$xV$md cSXCBb?3$ epVbyU^Q8
f&SNY66TY 7SGgrg8fh 8M>R=BK7%
bGn7$vrVq 9ERPfXAFy aP?@vG9@2
c?A2xq90G DsYPF%<uw K^%TuW@Eb
ad=AFceY4 eG+fTqCR4 gu#yDV^>#
gUgf4FWk+ x2ncBXm7D BcM054mAu
6&65nH=$$ =wN$Vv$fT rxqQM7>vX
99FkycbF8 hkcx+q63X +wPGh76bX
aQT?AD%b@ cdc+efzZg dLGwz7@L@
Requirements:
JASS:
library Scrambler uses /* v3.0.0.2
*************************************************************************************
*
* Scrambles/Shuffles a BigInt given a player id.
*
* This applies a StringHash to the player, converts that hash into base 8, and
* then uses that as a key. The end result is that each player's code (when
* this is used for save/load) is scrambled/shuffled differently.
*
* Scrambling:
* Shuffles in a specific base a number of times.
* Useful for mixing up save/load keys (the character sets)
* Recommend including front digit as data won't be lost for most uses
* Shuffling:
* Shuffles through a set of prime bases a number of times.
* Useful for mixing up save/load codes
*
*************************************************************************************
*
* REQUIREMENTS
*
* */ BigInt /*hiveworkshop.com/forums/jass-functions-413/system-bigint-188973/
*
************************************************************************************
*
* SETTINGS
*/
globals
/*************************************************************************************
*
* SALT
*
* Refers to what to append to the player's name when generating scrambler keys
* for each player. This is essentially like a password. It's impossible for another
* map to descramble a number without this password.
*
* This password can be any string, such as "7/ah+53~r\\ZZ"
*
*************************************************************************************/
private constant string SALT = ""
endglobals
/*************************************************************************************
*
* SetShuffleOrder
*
* Creates the shuffling algorithm using 5 different prime bases.
*
* Shuffling mixes a number up using a variety of bases to produce results rivaling
* top random number generators.
*
* Different bases ensure better mixes.
*
* Enabled Bases:
*
* 2
* Will shuffle number in groups of 1 bit
* 3
* Will shuffle number in groups of 1.58 bits
* 5
* Will shuffle number in groups of 2.32 bits
* 7
* Will shuffle in groups of 2.81 bits
* 11
* Will shuffle in groups of 3.46 bits
*
* Strategies:
*
* 1.
* shuffle by large1, shuffle by small1, shuffle by large2, etc
* 2.
* shuffle by small1, shuffle by large1, shuffle by small2, etc
* 3.
* shuffle by small1, shuffle by small2, shuffle by large1, shuffle by small3, etc
*
* Keep in mind that as fractional bits are shuffled, bits split/merge, meaning that the number
* can drastically change by becoming much smaller or much larger.
*
*
* Shuffle Array: so
*
* Ex:
*
* set so[0]=3 //first mix by 1.58 bits
* set so[1]=2 //second mix by 1 bit
* set so[2]=7 //third mix by 2.81 bits
* set so[3]=2 //fourth mix by 1 bit
*
* return 4 //return number of mixes
*
*************************************************************************************/
private keyword so
private function SetShuffleOrder takes nothing returns integer
/*************************************************************************************
*
* MIXES
*
* array: so
* bases: 2,3,5,7,11
*
*************************************************************************************/
set so[0]=5
set so[1]=2
set so[2]=3
set so[3]=11
set so[4]=2
set so[5]=7
set so[6]=3
/*************************************************************************************
*
* MIX COUNT
*
*************************************************************************************/
return 7
endfunction
/*
************************************************************************************
*
* function Scramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
* function Unscramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* function Shuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
* function Unshuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
************************************************************************************
*
* function Scramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* Scrambles a BigInt at the binary level.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to shuffle number (must be > 0)
* baseToScrambleIn: what base to scramble number in
*
************************************************************************************
*
* function Unscramble takes BigInt intToScramble, integer forPlayerId, integer shuffles, Base baseToScrambleIn, boolean includeFront returns nothing
*
* Unscrambles a BigInt at the binary level.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to unshuffle number
* baseToScrambleIn: what base to shuffle number in
* includeFront: this determines whether to include front number or not
*
* including the front number isn't advisable as 0s
* may move to the front, meaning data can be lost
*
* Scramble(1000) -> 0001, or 1
*
************************************************************************************
*
* function Shuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
* Scrambles a BigInt using user-defined algorithm.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to shuffle number (must be > 0)
*
************************************************************************************
*
* function Unshuffle takes BigInt intToScramble, integer forPlayerId, integer shuffles returns nothing
*
* Scrambles a BigInt using user-defined algorithm.
*
* intToScramble: BigInt
* forPlayerId: id of human player*
* shuffles: how many times to unshuffle number
*
************************************************************************************/
globals
private integer array ss
private boolean array se
private BigInt array d
private integer i
private integer dc
private integer k
private integer s1
private integer s2
private integer s3
private integer pid
private trigger mt=CreateTrigger() //mix trigger
private trigger dt=CreateTrigger() //demix trigger
private trigger st=CreateTrigger() //scramble trigger
private trigger ut=CreateTrigger() //unscramble trigger
private BigInt bi
private Base array bs
private integer array so
private integer sc=0
endglobals
private function LNF takes BigInt int,boolean i0 returns nothing
set dc=0
if (i0) then
loop
set int=int.next
exitwhen int.end
set d[dc]=int
set dc=dc+1
endloop
else
loop
set int=int.next
exitwhen int.next.end
set d[dc]=int
set dc=dc + 1
endloop
set int=int.next
endif
endfunction
private function LNB takes BigInt int,boolean i0 returns nothing
set dc=0
if (not i0) then
set int=int.previous
endif
loop
set int=int.previous
exitwhen int.end
set d[dc]=int
set dc=dc+1
endloop
endfunction
private function FLP takes integer id,integer i2 returns nothing
//find last position
loop
exitwhen 0==i2
set s1=dc
loop
exitwhen 0==s1
set s1=s1-1
if (se[k]) then
set k=ss[id]
else
set k=k+1
endif
endloop
set i2=i2-1
endloop
endfunction
function Scramble takes BigInt int,integer id,integer shuffles,Base bb,boolean i0 returns nothing
local Base b=int.base
set pid=id
set k=ss[id]
set i=shuffles
debug if (0!=ss[id]) then
if (b!=bb) then
set int.base=bb
endif
//load number
call LNF(int,i0)
//scramble
call TriggerEvaluate(st)
if (b!=bb) then
set int.base=b
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
function Unscramble takes BigInt int,integer id,integer shuffles,integer bb,boolean i0 returns nothing
local Base b=int.base
set i=shuffles
set pid=id
set k=ss[id]
debug if (0!=ss[id]) then
if (b!=bb) then
set int.base=bb
endif
//load number
call LNB(int,i0)
//retrieve last position
call FLP(id,shuffles)
//unscramble
call TriggerEvaluate(ut)
if (b!=bb) then
set int.base=b
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
//scramble
private function St takes nothing returns boolean
loop
exitwhen 0==i //exitwhen no more shuffles
set s1=dc //loop through digits from left-1 to right
//don't shuffle left most to save a bit as
//left most must be 1
loop
exitwhen 0==s1 //exitwhen no more digits
set s1=s1-1 //shift down as array ends at n-1
//current digit slot - hash digit (shift right)
set s2=s1-ss[k]
//if s2 is negative, add total digits until positive
loop
exitwhen 0<=s2
set s2=dc+s2
endloop
//swap s2 and s1
set s3=d[s2].digit
set d[s2].digit=d[s1].digit
set d[s1].digit=s3
//if out of digits, go back to first digit on hash
//otherwise, go to next digit
//last existing digit is marked as end
if (se[k]) then
set k=ss[pid]
else
set k=k+1
endif
endloop
set i=i-1
endloop
return false
endfunction
//unscramble
private function Ut takes nothing returns boolean
//go backwards
loop
exitwhen 0==i
set s1 = dc
loop
exitwhen 0==s1
set s1=s1-1
set k=k-1
if (0==ss[k]) then
set k=ss[pid+12]
endif
set s2=s1+ss[k]
loop
exitwhen s2<dc
set s2=s2-dc
endloop
set s3=d[s2].digit
set d[s2].digit=d[s1].digit
set d[s1].digit=s3
endloop
set i=i-1
endloop
return false
endfunction
//shuffle
private function Mt takes nothing returns boolean
local integer sh=0
set k=ss[pid]
loop
exitwhen sh==sc
set i=1
set bi.base=bs[so[sh]]
call LNF(bi,false)
call St()
set sh=sh+1
set k=ss[pid]
endloop
return false
endfunction
//unshuffle
private function Dt takes nothing returns boolean
local integer sh=sc
set k=ss[pid]
loop
exitwhen 0==sh
set sh=sh-1
set i=1
set bi.base=bs[so[sh]]
call LNB(bi,false)
call FLP(pid,1)
call Ut()
set k=ss[pid]
endloop
return false
endfunction
function Shuffle takes BigInt int, integer id, integer h returns nothing
local Base b=int.base
debug if (0!=ss[id]) then
set bi=int
set pid=id
loop
exitwhen 0==h
call TriggerEvaluate(mt)
set h=h-1
endloop
set int.base=b
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
function Unshuffle takes BigInt int, integer id, integer h returns nothing
local Base b=int.base
debug if (0!=ss[id]) then
set bi=int
set pid=id
loop
exitwhen 0==h
call TriggerEvaluate(dt)
set h=h-1
endloop
set int.base=b
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"SCRAMBLER ERROR: INVALID PLAYER "+I2S(id))
debug endif
endfunction
private module Init
private static method onInit takes nothing returns nothing
local integer is=11
local integer hh
local integer ks=25
local Base b8=Base["012345678"]
local BigInt bg
call TriggerAddCondition(mt,Condition(function Mt))
call TriggerAddCondition(dt,Condition(function Dt))
call TriggerAddCondition(st,Condition(function St))
call TriggerAddCondition(ut,Condition(function Ut))
set bs[2]=Base["01"]
set bs[3]=Base["012"]
set bs[5]=Base["01234"]
set bs[7]=Base["0123456"]
set bs[11]=Base["0123456789A"]
set sc=SetShuffleOrder()
loop
if (GetPlayerSlotState(Player(is))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(is))==MAP_CONTROL_USER) then
set ss[is]=ks
set hh=StringHash(StringCase(GetPlayerName(Player(is))+SALT,false))
if (0>hh) then
set hh=-hh
endif
set bg=BigInt.create(b8)
call bg.add(hh,0)
set bg=bg.previous
loop
set ss[ks]=bg.digit+1
set bg=bg.previous
exitwhen bg.end
set ks=ks+1
endloop
set se[ks]=true
set ss[is+12]=ks
call bg.destroy()
set ks=ks+2
endif
exitwhen 0==is
set is=is-1
endloop
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
Demo
JASS:
globals
constant boolean SCRAMBLE=false
constant boolean DESHUFFLE=false
constant integer SHUFFLES=1
constant string NUMBER="65536"
constant integer RUNS=150
constant integer NUM_PER_ROW=10
constant string BASE="0123456789"
constant string SCRAMBLE_BASE="01"
endglobals
struct tester extends array
private static string c=NUMBER
private static integer m=RUNS
private static integer ad=0
private static method r takes nothing returns nothing
local Base b10=Base[BASE]
local Base b3=Base[SCRAMBLE_BASE]
local BigInt i
local string s=""
local integer a=NUM_PER_ROW
call DestroyTimer(GetExpiredTimer())
loop
exitwhen m==0
set i=BigInt.convertString(c,b10)
call i.add(ad,0)
static if SCRAMBLE then
call Scramble(i,0,SHUFFLES,b3,false)
else
call Shuffle(i,0,SHUFFLES)
endif
static if DESHUFFLE then
static if SCRAMBLE then
call Unscramble(i,0,SHUFFLES,b3,false)
else
call Unshuffle(i,0,SHUFFLES)
endif
endif
set s=s+" "+i.toString()
set a=a-1
if (a==0) then
set a=NUM_PER_ROW
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,6000,s)
set s=""
endif
set ad=ad+1
set m=m-1
call i.destroy()
endloop
if (a<NUM_PER_ROW) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,6000,s)
endif
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function thistype.r)
endmethod
endstruct