Mastering Algorithms with Perl phần 8 pot

74 192 0
Mastering Algorithms with Perl phần 8 pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

# all clear, add it to the product $prod *= $_; } @inverses = map { my $k = $prod / $_; $k * mod_inverse( $k, $_ ); } @_; } # Convert from a list of remainders into an integer. sub from_chinese { use integer; my $v = shift; my $t = 0; for (0 $#bases) { $t += $inverses[$_] * $v->[$_]; } return $t % $prod; } # Convert from an integer into a list of remainders. sub to_chinese { use integer; my $v = shift; my @v = map { $v%$_ } @bases; return \@v; } How many eggs did the woman have? set_chinese(3, 4, 5, 7); print from_chinese( [1,1,1,0] ); # prints 301 Treating Chinese Remainders As Integers Just as you can add, subtract, and multiply regular integers, you can do the same with Chinese remainders. These representations could be manipulated using routines such as add_chinese(), which adds two Chinese representations:break # Add two Chinese remainder lists. sub add_chinese { use integer; my ($v1, $v2) = @_; my @v = map { ($v1->[$_] + $v2->[$_]) % $bases[$_] } 0 $#bases; return \@v; } Page 517 By changing the + to a * or -, you can define multiply_chinese() and subtract_chinese() similarly. Since adding, subtracting, and multiplying remainders yields the same result as adding, subtracting, or multiplying the corresponding integers, we can perform arithmetic with whichever representation we like. Integer Exponentiation Perl has a built-in exponentiation operator, **, but it's useful to understand how exponentiation works for integers so that we can modify it for modular arithmetic. Here's a simple-minded subroutine that computes i j : sub exp_slow { use integer; my ( $result, $i, $j ) = (1, @_); $result *= $i while $j ; return $result; } That's fine when the numbers are small, but what about when you are dealing with hundred digit numbers? That while loop will run until the universe collapses. Fortunately, we don't have to perform the multiplications one by one, thanks to the following identity: $x**$y * $x**$z == $x**($y+$z) Here is one variation that uses that identity. It uses the fact that if j is even, it can be written as 2k and i 2k =i k *i k . If j is odd, it can be written as 2k + 1 and i 2k+1 =i k *i k *i. sub exp_recurse { use integer; my ( $bottom, $i, $j ) = ( 1, @_ ); return $i - $1 + 1 if $j == 0; return $i if $j == 1; if ( $j % 2 ) { # Is $j odd? $bottom = $i; $j; } my $halftop = exp_recurse( $i, $j/2 ); return $halftop * $halftop * $bottom; } There is one oddity in this subroutine: we wrote return $i - $i + 1 if $j == 0; instead of this much simpler and seemingly equivalent formulation:break return 1 if $j == 0; Page 518 There is method to this madness. The scalar $i might not have been provided as a simple scalar. The caller might have used a package such as Math::BigInt or SSLeay::BN (discussed in the section "Very Big, Very Small, and Very Precise Numbers" in Chapter 11, Number Systems) for $i or $j. Our subroutine ensures with that expression, that the value it returns is the same type as $i whether that was a Perl scalar integer, a Math::BigInt, or an SSLeay::BN, etc. exp_recurse() performs fewer multiplications, so you would expect it to be faster than exp_slow(). It's actually slower for most inputs because of the recursion. exp_fast() avoids the recursion: sub exp_fast { use integer; my ( $i, $j ) = @_; my $result = $i-$i+1; my $pow2 = $i; while ( $j ) { if ( $j%2 ) { $result = $pow2 * $result; $j ; } $j /= 2; $pow2 = $pow2 * $pow2; } return $result; } Tested on a 199-MHz DEC station running OSF/1 with 96 MB RAM with integers chosen randomly between 1 and 100 for both $i and $j, the timings are: exp_slow: 19 secs ( 9.08 usr 0.03 sys = 9.12 cpu) exp_recurse: 28 secs (11.72 usr 0.05 sys = 11.77 cpu) exp_fast: 17 secs ( 5.53 usr 0.08 sys = 5.62 cpu) exp_fast() computes (in $pow2) i 1 , i 2 , i 4 , i 8 , and so on. We multiply together (into $result) the powers of $i that correspond to the "on" bits in $j. For example, if $j is 13, it is 1101 in binary, so it's equal to 2 3 + 2 2 + 2 0 . Here are the intermediate values of $result, $pow2, and $j for each time we come to the top of the loop:break iteration $result $pow2 $j Use $pow2? 0 1 $i 1101 yes 1 $i $i**2 110 no 2 $i $i**4 11 yes 3 $i**5 $i**8 1 yes 4 $i**13 $i**16 0 no Page 519 Modular Exponentiation Now back to modular arithmetic where we find a very useful application of our fast exponentiation. It is not hard to convert exp_fast() to perform modular exponentiation. While we're at it, we'll tweak the code to avoid calculating the final unused value of $pow2. # $i ** $j (mod $n) sub exp_mod { use integer; my ( $i, $j, $n ) = @_; my $result = $i - $i + 1; return $result unless $j; my $pow2 = $i; while ( 1 ) { if ( $j%2 ) { $result = ($pow2 * $result) % $n; return $result unless $j; } $j /= 2; $pow2 = ($pow2 * $pow2) % $n; } } Just as we looked at tables for modular multiplication, it is instructive to look at tables for modular exponentiation. Here are tables for i k (mod 5) and i k (mod 6). Each row is a different i and each column a different k.break ** 1 2 3 4 5 0 0 0 0 0 0 1 1 1 1 1 1 2 2 4 3 1 2 3 3 4 2 1 3 4 4 1 4 1 4 ** 1 2 3 4 5 6 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 4 2 4 2 4 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 1 5 1 5 1 Page 520 When the modulus n is prime, i (n-1) (mod n) is always 1 for nonzero values of i. If you can find a value of i for which i (n-1) (mod n) is not equal to 1, you have shown that n is composite. * If we pick some value i and determine that i (n-1) (mod n) is equal to 1, then we call i a witness to the possibility that n is prime. A witness does not prove that a number is prime. However, if the number is composite and very large, a randomly chosen number will act as a witness to it being prime less than one time in a thousand; the other 999 times it will prove that the number is not prime. Miller-Rabin: Prime Generation Revisited By combining modular exponentiation with these additional ways of determining pnmality, we're ready to provide a prime number testing function adapted for very large numbers. The Miller-Rabin test determines primality by choosing a random number, $witness, and raising it to the power $n - 1. If the result is not 1, then $n cannot be prime. If the answer is 1, we can't be completely certain that $n is prime, but it provides very high confidence. Even for quite small values of $n, there is at most a 25% chance that a randomly chosen number will fail to prove a composite number is not prime. However, even with the very high certainty that you get with a 1 on large numbers, Bruce Schneier (in Applied Cryptography) recommends testing 5 different randomly chosen numbers to be sure. (It still could be wrong, but it's about as likely as spontaneous human combustion.)break # Check whether $n is prime, by trying up to 5 random tests. sub is_prime { use integer; my $n = shift; my $n1 = $n - 1; my $one = $n - $nl; # 1, but ensure the right type of number. my $witness = $one * 100; # find the power of two for the top bit of $n1. my $p2 = $one; my $p2index = -1; ++$p2index, $p2 *= 2 while $p2 <= $n1; $p2 /= 2; # number of iterations: 5 for 260-bit number, go up to # 25 for much smaller numbers. my $last_witness = 5; $last_witness += (260 - $p2index)/13 if $p2index < 260; * The proof is shown in Introduction to Algorithm by Cormen et al. Page 521 for $witness_count ( 1 $last_witness ) { $witness *= 1024; $witness += rand(1024); $witness = $witness % $n if $witness > $n; $witness = $one * 100, redo if $witness == 0; my $prod = $one; my $nlbits = $n1; my $p2next = $p2; # compute $witness ** ($n - 1). while (1) { # Is $prod, the power so far, a square root of 1? # (plus or minus 1) my $rootone = $prod == 1 || $prod == $n1; $prod = ($prod * $prod) % $n; # An extra root of 1 disproves the primality. return 0 if $prod == 1 && !$rootone; if ( $n1bits >= $p2next ) { $prod = ($prod * $witness) % $n; $n1bits -= $p2next; } last if $p2next == 1; $p2next /= 2; } return 0 unless $prod == 1; } return 1; } The is_prime() function tells you whether a number is prime. However, it can't tell you the factors if it's composite. The earlier prime testing routines can all be easily modified to return the factors of a composite number because, unlike is_prime(), they all worked by testing all possible factors in some way. If you use the SSLeay::BN package, it has some convenient built-in methods for manipulating primes and other integers. Some, like gcd(), correspond to functions we have already discussed. Its is_prime() method tests a number for primality much like the previous routine (but faster). There is also a function which generates a prime number of a specified size:break use SSLeay::BN: $prime = SSLeay::BN::generate_prime( $num_bits, 0 ); $p_minus_1 = $prime 1; if ( $p_minus_1->gcd(3) != 0 ) { # . . . } $p100 = $prime + 100; Page 522 if ( $p100->is_prime ) { # . . . } There is no known algorithm for finding the factors of a large composite number n that has the same order of speed as is_prime(). All known methods have exponential growth like the earlier prime testing functions—too slow to be depended upon. There is a heuristic algorithm (Pollard-Rho) that will (usually) find a factor in time proportional to the square root of that factor. It can quickly discover small factors if there are any, but if all of the factors are very large, they'll only be found by luck. Factors can be as large as , finding one that large takes time on the order of . The difficulty in factoring numbers is a premise of one of the most powerful encryption methods known to man—RSA public key encryption, which appears in Chapter 13, Cryptography. Unsolved Problems This section introduces three readily understood number theory problems that remain unsolved, despite tens of thousands of mathematician-hours spent trying to prove (or disprove) them. Each has been encapsulated in a Perl program—one that can't prove the conjecture, but given enough time, just might disprove it. You might assume that all of these conjectures are true because mathematicians through the ages haven't been able to solve them. But consider:in 1769, Euler conjectured that there were no solutions to: for a, b, c, and d ≥ 1. However it was proven in 1987 that there are an infinite number of solutions, of which this is the smallest: You can test each of the programs that follow by wrapping it in code that selects numbers to test. (Each of these routines takes the number to be tested as its argument, of course.) We won't do that for you; there is an infinite number of numbers that could be tested and you'll want to come up with your own test order—one that that doesn't overlap with ranges chosen by other people. Feel free to start at 1 and work upward. Better yet, start at twenty billion, since all of these problems have been tested that up to that level. If any of these programs succeeds in disproving a conjecture it beeps. It keeps beeping until you interrupt it, thanks to the print "\a" while 1. Yes it's annoying, but solving these famous problems merits a little fanfare.break Page 523 Is the Collatz Conjecture False? Take a natural number. If it's even, halve it. If it's odd, triple it and add one. Repeat. This procedure usually hits the cycle 4, 2, 1, 4, 2, 1, . . . Will it always do that? No one knows. That's the Collatz conjecture. Here's a program that tests it: # Conjecture: this program returns 1 for all positive # integers $n > 1. If this program makes noise, the # Collatz problem will have been solved and its associated # conjecture refuted. # # Uncomment the third line if you're actually trying to # disprove the Collatz conjecture. # sub collatz { use integer; my ($n) = shift; # return unless $n > 7ell; # Already tested; don't bother! while ($n != 1) { print "$n "; if ($seen{$n}) { print "COLLATZ CONJECTURE REFUTED with $n.\n"; print "\a" while 1; } $seen{$n} = 1; if ($n % 2) { $n *= 3; $n++; } else { $n /= 2 } } } Is There an Odd Perfect Number? A perfect number is an integer whose factors sum to itself. Six is a perfect number, because its factors are 1, 2, and 3, and 1 + 2 + 3 = 6. The first four perfect numbers are 6, 28, 496, and 8,128. No one's ever found an odd perfect number; if one exists, this program will—given enough time and memory.break # Conjecture: there are no odd perfect numbers. # # Uncomment the two "return" lines below if you're # really searching for odd perfect numbers. # sub perfect { my $n = shift; my $n_orig = $n; my $n2 = $n * 2; my $fact_sum = 1; Page 524 # return 0 unless $n % 2; # Even number; don't test. # return 0 unless $n > le300; # Already tested; don't bother. for ( my $i = 0; my $p = prime($i); ++$i ) { # compute: 1 + $p + $p**2 + . . . # up to the highest power of $p that divides $n my $pow_sum = 1; my $pow = 1; while ( ($n%$p) == 0 ) { $pow *= $p; $pow_sum += $pow; $n /= $p; } # That's all the factors that are powers of $p. # For every previous determined factor, there is one # different factor for each different power of $p found # (including p**0 == 1). The sum of all known factors # is thus multiplied by $pow_sum. We never actually # need to record the actual values of the factors. # Eventually, our sum will include the original value of # $n. That's why we look for $n2 as the target to indicate # a perfect number. If we exceed $n2, we can quit without # finishing the factorization. # $fact_sum *= $pow_sum; last if $fact_sum > $n2; last if $n <= 1; } if ($fact_sum == $n2) { print "Perfect number ($n_orig).\n"; if ($n_orig % 2) { print "ODD PERFECT NUMBER FOUND.\n"; print "\a" while 1; } return 1; } return 0; } Is the Goldbach Conjecture False? The Goldbach conjecture contends that every even number can be expressed as the sum of two primes. For instance, 12 is 5 + 7, 14 is 7 + 7, and 18 is 13 + 5. The following program searches for numbers that refute the conjecture. It uses the primes() subroutine from the section "Prime Numbers."break sub goldbach { use integer; my ($n) = shift; my ($low, $high, $primes); ($primes, $high) = primes($n); # Shown earlier in chapter. Page 525 $low = 0; # return 1 unless $n > 2el0; # Already tested; don't bother. # # (But primes() will cause problems # # if you go far beyond this point.) return if $n % 2; # Return if the number is odd. while($low <= $high ) { my $total = $primes->[$low] + $primes->[$high]; if ($total == $n) { return ($primes->[$low], $primes->[$high]); } elsif ($total < $n) { ++$low; } else { $high; } } print "GOLDBACH CONJECTURE REFUTED: $n\n"; print "\a" while 1; } Rather than trying all pairs of primes in the range, we used a single scan from both ends of the array of primes. When the sum of the two primes we're currently looking at is too high, the prime at the top end can't use the current prime at the low end or any higher prime to add up to the target, and we'll have already dealt with the possibility of it making a pair with a smaller prime closer to the front of the array. So, we can stop worrying about this high prime and move to its predecessor. Similarily, if the sum is too small, we forget about the low end prime and move to its successor. Of course, if the sum is equal, we've found that the current target can be represented d& the sum of a pair of primes and return them. Here's a sample run:break print "992 is ", joint (' + ', goldbach(992)), "\n"; 992 is 919 + 73 Page 526 13— Cryptography Quis custodiet ipsos custodes? —Juvenal (c. 60–140), Satires Do you lock your door when you leave the house? Do you write personal information on a postcard? Do you pay bills without verifying the amounts? Cryptography is the science of ensuring that important activities can only be carried out by authorized entities. There are many variations on this theme: identification, permissions, and, of course, secrecy. Cryptography is diverse, because there are many ways that people and computers can gain unauthorized access to data. The access can be passive (reading data) or active (transforming data). This chapter deals with a number of topics which may seem independent, but there are frequent and surprising connections among them. They have a lot to do with number theory, probability (especially random numbers), and compression (which treats redundancy as an opportunity, while cryptography treats redundancy as a hazard). Some of the topics are: • Logging on to a computer • Determining whether a file's contents have changed [...]... can do the same in Perl (e.g., $r1^=$r2; $r2^=$r1; $r1^=$r2;), but for simple scalar values it's clearer to just write the operation as ($r1, $r2) = ($r2, $r1); and let Perl worry about its own optimizations book, and XORs those pad bits with the transmitted message to recreate the original plain message It is important that the same bits never be used again—two messages encrypted with the same random... candidate algorithms currently undergoing scrutiny The DES-EDE and DES-EDE3 algorithms mentioned earlier use DES as a building block to compose an encryption that is more secure than DES alone, making it once again hard to crack Analysis of Shared-Secret Encryption Exhaustive-search breaking of the 40-bit export-approved algorithms doesn't even require government-backed resources—don't use these algorithms. .. but that particular patent has expired, so public key encryption with an algorithm other than RSA is possible One method that can be used is El Gamal.break Page 553 There are two applications of El Gamal It can be used for public key encryption (encrypting with the public key and decrypting with the private key only—you cannot encrypt with the public key instead) Additionally, it can be used as a signature... produce good algorithms, and those can be exported, so it is usually easy to find code written outside the U.S that implements an NSA-restricted scheme.) The export restrictions have also kept companies in other countries from producing encryption products Since international communication is so widespread, it's important to have the same software everywhere.break Page 5 28 Authorizing People with Passwords... $pos; return ($msg ^ $key); } That still leaves you with the job of generating the pad file Do not use Perl' s rand function—for this purpose you need something that is really random, not just a pseudorandom sequence that repeats after a short time (Just how short the time is depends upon which rand function was built into your particular copy of Perl and how you use it to generate a bitstream) A one-time... the algorithm you use is one of the attack methods that will surely be attempted.) So, what algorithms have undergone serious scrutiny and emerged unbroken? We'll be presenting algorithms provided in the SSLeay module, written by Eric Young available from ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL The encryption algorithms provided are: DES The Data Encryption Standard, described in more detail later; patent... decrypting with key 2 in the middle stage and encrypting with key 1 in the first and last stages EDE3 uses three keys instead of repeating one in the first and last stages Unlike the basic DES algorithm, neither EDE nor EDE3 are easily breakable by brute force IDEA The International Data Encryption Algorithm; patented (no license fee required for noncommercial use) RC2, RC4, and RC5 Various algorithms. .. system administrators to crack over 50% of user passwords this way.) A Perl program to provide a portion of crack's functionality can be very short; in fact, it can be squashed into a single line: perl -nle 'setpwent;crypt($_,$c)eq$c&&print"$u $_"while($u,$c)=getpwent' Fans of readability will take a few more lines:break #!/bin /perl -nle setpwent; $inform_type = shift || 'display'; while ( ($u,$c)... problem IDEA is believed to be reasonably secure and will often be your preferred choice It is slightly harder than Triple-DES to break by brute force (21 28 instead of 2112, albeit with an easier attack method), and IDEA runs much faster IDEA is used within the popular PGP (Pretty Good Privacy) package The hazard of reusing the key means that there are some practical limitations in using a shared-secret... to allow each person to get a copy of this book Public key encryption provides a better alternative Encrypting with SSLeay Here's an example of using DES encryption from Perl, using the SSLeay module:break use SSLeay; # Convert 64-bit key from ASCII hex character form: my $keyASCII = "0123456 789 abcdef"; # but this "key" is rather obvious my $key = pack("H16", $keyASCII); Page 546 # Set up the DES engine . running OSF/1 with 96 MB RAM with integers chosen randomly between 1 and 100 for both $i and $j, the timings are: exp_slow: 19 secs ( 9. 08 usr 0.03 sys = 9.12 cpu) exp_recurse: 28 secs (11.72. its factors are 1, 2, and 3, and 1 + 2 + 3 = 6. The first four perfect numbers are 6, 28, 496, and 8, 1 28. No one's ever found an odd perfect number; if one exists, this program will—given enough. of Programming Perl. It is not in the second edition,continue Page 532 but the program is still available by FTP from O'Reilly: ftp://ftp.ora.com/pub/examples/nutshell/programming -perl/ perl.tar.Z. * There

Ngày đăng: 12/08/2014, 21:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan