Advertisement
musifter

Untitled

Dec 22nd, 2019
530
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 2.71 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. use bignum;
  7. use ntheory 'invmod';
  8.  
  9. use constant CARD_POS  => 2020;
  10.  
  11. #use constant DECK_SIZE => 10007;
  12. use constant DECK_SIZE => 119315717514047;
  13.  
  14. use constant SHUFFLES  => 101741582076661;
  15.  
  16. # Linear function representing shuffle: f(x) = $func[0] * x + $func[1]
  17. my @func = (1, 0);
  18.  
  19. while (<>) {
  20.     chomp;
  21.  
  22.     if (m#^deal into new stack#) {
  23.         # compose (-x - 1) with Ax+B, mod N
  24.         # -(Ax+B) - 1 => -Ax + (-B - 1)
  25.         @func = (-$func[0] % DECK_SIZE, (-1 - $func[1]) % DECK_SIZE);
  26.  
  27.     } elsif (m#^cut (-?\d+)#) {
  28.         # compose (x - c) with Ax+B, mod N
  29.         # (Ax+B) - c => Ax + (B - c)
  30.         @func = ($func[0], ($func[1] - $1) % DECK_SIZE);
  31.  
  32.     } elsif (m#^deal with increment (\d+)#) {
  33.         # compose mx with Ax+B, mod N
  34.         # m(Ax+B) = (Am)x + Bm
  35.         @func = (($func[0] * $1) % DECK_SIZE, ($func[1] * $1) % DECK_SIZE);
  36.  
  37.     } else {
  38.         die "Unrecognized line: $_\n";
  39.     }
  40. }
  41.  
  42. print "f(x) = $func[0]x + $func[1]\n";
  43.  
  44. my $fx = ($func[0] * 2019 + $func[1]) % DECK_SIZE;
  45. print "Card 2019 is at position $fx\n";
  46.  
  47. #
  48. #   f(x) = y mod N, need to find f_inverse to get x = f_inv(y) mod N
  49. # Ax + B = y mod N
  50. #     Ax = (y - B) mod N
  51. #      x = A_inv * (y - B) mod N, A_inv is inverse of A in mod N
  52. #
  53. my $y = (invmod( $func[0], DECK_SIZE ) * ($fx - $func[1])) % DECK_SIZE;
  54. print "Card at position $fx is $y\n\n";
  55.  
  56. #
  57. # Recursive function to compose linear function n times.
  58. #
  59. sub apply_n_times {
  60.     my ($func, $n) = @_;
  61.  
  62.     return ($func->[0], $func->[1])  if ($n == 1);
  63.  
  64.     # divide and conqueur, get the function for half, h(x) = Ax + B
  65.     my ($A, $B) = &apply_n_times( $func, int( $n / 2 ) );
  66.  
  67.     #
  68.     # h(h(x)) = (A(Ax + B) + B
  69.     #         = A^2x + AB + B
  70.     #         = A'x + B'
  71.     #
  72.     ($A, $B) = (($A * $A) % DECK_SIZE, ($B * ($A + 1)) % DECK_SIZE);
  73.  
  74.     #
  75.     # Let f(x) = Cx + D
  76.     #
  77.     # h(h(x)) o f(x) = A'(Cx + D) + B'
  78.     #                = (A'C)x + A'D + B'
  79.     #
  80.     if ($n % 2 == 1) {
  81.         ($A, $B) = (($A * $func->[0]) % DECK_SIZE, $A * $func->[1] + $B);
  82.     }
  83.  
  84.     return ($A, $B);
  85. }
  86.  
  87. #
  88. # 2423 is 101741582076661 % 10006, if testing with 10007 card deck, this
  89. # should be the position in the first cycle that's congruent, so it
  90. # should be equal to the big shuffle.
  91. #
  92. foreach my $i (1 .. 8, 2423) {
  93.     my ($A, $B) = &apply_n_times( \@func, $i );
  94.     $y = (invmod( $A, DECK_SIZE ) * (CARD_POS - $B)) % DECK_SIZE;
  95.     print "Card at position ", CARD_POS, " after $i shuffles: $y\n";
  96. }
  97.  
  98. my ($A, $B) = &apply_n_times( \@func, SHUFFLES );
  99. $y = (invmod( $A, DECK_SIZE ) * (2020 - $B)) % DECK_SIZE;
  100. print "Card at position ", CARD_POS, " after ", SHUFFLES, " shuffles: $y\n";
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement