Write a computer program to determine the greatest common divisor of two integers and and to express as for some integers and .
I will be writing my programs in Haskell.
First the gcd program. I have to admit that I wrote my own gcd function, but was disappointed by the performance when compared to the library gcd that is built in to Haskell. My own code took about four times as much time and twice as much space. After tweaking for a while I got the performance to twice the time. I finally peeked at the Prelude definition to see what it did differently, to find out that the only major difference was the use of “rem” (for integer division remainder) instead of “mod”. FYI: rem is considerably more efficient than mod. I do disagree with the library function on one point, however; I feel very strongly that should be 0 and not undefined, as the Haskell specification says. First of all, it satisfies the definition rather trivially. We have since for all integers , and if , then . Second, this has the side effect of making gcd idempotent on . Perhaps that’s why the Haskellers don’t like it.
Anyway, here is the code for gcd.
This function uses about 15 reductions and 20 cells per step. (“Reduction” and “cell” are the fancy words Haskell uses to measure time and space complexity, respectively.) By the way, I found this number by computing the gcd of consecutive Fibonacci numbers; I forget the reason, but Fibonacci numbers give the worst case time complexity (for their size) for this implementation of the gcd function.
Now for the second program. What this program needs to find are two integers which satisfy Bezout’s identity for and . For this reason I’ll call the function “bezout”. Here is my code.
The bezout function went through several iterations while I picked out bugs. To give us some confidence that this function does what we think it does, we can run a simple test.
Without going into too much detail, Haskell has a library called QuickCheck that takes a function that returns either True or False, generates a bunch of random input, and makes sure that the function returns True for all the input. To run the tests, simply run one of the following commands in your favorite Haskell interpreter.
I will be writing my programs in Haskell.
First the gcd program. I have to admit that I wrote my own gcd function, but was disappointed by the performance when compared to the library gcd that is built in to Haskell. My own code took about four times as much time and twice as much space. After tweaking for a while I got the performance to twice the time. I finally peeked at the Prelude definition to see what it did differently, to find out that the only major difference was the use of “rem” (for integer division remainder) instead of “mod”. FYI: rem is considerably more efficient than mod. I do disagree with the library function on one point, however; I feel very strongly that should be 0 and not undefined, as the Haskell specification says. First of all, it satisfies the definition rather trivially. We have since for all integers , and if , then . Second, this has the side effect of making gcd idempotent on . Perhaps that’s why the Haskellers don’t like it.
Anyway, here is the code for gcd.
1
2
3
4
| {- Compute the GCD of a and b. -} gcd :: Integer -> Integer -> Integer gcd a 0 = abs a gcd a b = gcd b (a `rem` b) |
Now for the second program. What this program needs to find are two integers which satisfy Bezout’s identity for and . For this reason I’ll call the function “bezout”. Here is my code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| swap (a,b) = (b,a) {- Find integers (q,r) so that a = qb + r and 0 <= r < |b|. -} divAlg :: Integer -> Integer -> (Integer, Integer) divAlg _ 0 = error "division by zero" divAlg a b = (quot a b, rem a b) {- Find integers (x,y) so that ax + by = gcd(a,b). -} bezout :: Integer -> Integer -> (Integer, Integer) bezout a b = ((signum a)*x,(signum b)*y) where (x,y) = bezout' (abs a) (abs b) where bezout' _ 0 = (1,0) bezout' _ 1 = (0,1) bezout' a b = (y, x - q*y) where (x,y) = bezout' b r (q,r) = divAlg a b |
1
2
3
4
| {- Verify the correctness of bezout on some input a b. -} bezoutTest :: Integer -> Integer -> Bool bezoutTest a b = (a*x) + (b*y) == d where d = gcd a b; (x,y) = bezout a b |
1
2
3
4
5
6
7
8
9
10
| import Test.QuickCheck {- Simple test on 100 random inputs. -} quickCheck bezoutTest {- Display each test input before checking it. -} verboseCheck bezoutTest {- With this command you can supply the number of tests to run. -} check (defaultConfig { configMaxTest = 10000}) bezoutTest |
No comments:
Post a Comment