Warning! This site is under development.
This class will be recorded! Its use will be limited to members of the class. Please do not share with others.
Approximate overview
Goal: Compute (x^n) mod m, where x, n, and m are all non-negative long
values.
Rationale: Useful for encryption and decryption.
Assume: x < m and m < sqrt(LONG_MAX).
Iterative solution: Use a for loop. Easy to verify correctness
Recursive solution 1:
x^(2k+1) = x * x^(2k)
Idea: Instead of *, use MULTIPLY. In normal situations,
MULTIPLY(X,Y) just gives X*Y (parenthesized appropriately).
When we want to count, we use something that increments a
counter and then returns X*Y.
#ifdef LOGMATH
extern long _MULTIPLICATIONS;
#define MULTIPLY(X,Y) ((_MULTIPLICATIONS++ * 0) + ((X)*(Y)))
#else
#define MULTIPLY(X,Y) ((X)*(Y))
#endif
How do we ensure that there’s only one copy of the variables?
extern in the header. That says “It’s not declared here.”
logmath.c).static (that’s what I did wrong).Some examples
$ ./emr 11 1000 51
16
*: 15
/: 9
%: 30
$ ./emr 11 2000 51
1
*: 16
/: 10
%: 32
$ ^2^4
./emr 11 4000 51
1
*: 17
/: 11
%: 34
$ ^4^8
./emr 11 8000 51
1
*: 18
/: 12
%: 36
And we’ve learned …?
Other ways to analyze?
(void *) for your
parameters.How can we turn that wonderful recursive solution into a slightly-less-wonderful, but somewhat-more-efficient iterative solution?
long
expmod (long x, long n, long m)
{
if (0 == n) // x^0 = 1
{
return 1;
}
else if ((n % 2) == 0) // even
{
return expmod ((x*x) % m, n/2, m); // x^(2k) = (x^2)^k; k = n/2
} // even
else // odd
{
long tmp = expmod (x, n-1, m); // x^(k+1) = x^k * x; k = n - 1
return (x * tmp) % m;
} // odd
} // expmod
How do you make a procedure tail recursive?
so-farlong
expmod (long x, long n, long m)
{
return expmod_helper (x, n, m, 1);
} // expmod (long x, long n, long m)
long
expmod_helper (long x, long n, long m, long acc)
{
if (0 == n)
{
return acc;
} // base case
else if (n % 2 == 0)
{
return expmod_helper ((x*x) % m, n/2, m, acc);
} // even
else
{
// x^(k+1) = x^k * x; k = n - 1
// return (x * expmod (x, n-1, m)) % m;
return expmod_helper (x, n-1, m, (x*acc)%m);
} // odd
} // expmod_helper
(define expmod
(lambda (x n m)
(letrec ([helper
(lambda (x n m so-far)
(cond
[(zero? n)
so-far]
[(even? n)
(helper (* x x) (/ n 2) m so-far)]
[else ; n is odd
(helper x (- n 1) m (* x so-far))]))])
(helper x n m 1))))
Whoo! It looks fast. And it works. And that’s probably the wrong order to look at things.
emh (x, n, m, acc)
emh (2, 11, LARGE, 1) // Our initial call
// n is odd
emh (2, 10, LARGE, 2*1)
// n is even
emh (4, 5, LARGE, 2*1) // x is 2^2
// n is odd
emh (4, 4, LARGE, 4*2*1)
// n is even
emh (16, 2, LARGE, 4*2*1) // x is 2^4
// n is even
emh (256, 1, LARGE, 4*2*1) // x is 2^8
// n is odd
emh (256, 0, LARGE, 256*4*2*1) // acc 2^8 * 2^2 * 2^1 * 1 = 2^11
// n is zero
return acc // 2^11
long
expmod (long x, long n, long m)
{
long result = 1;
while (n > 0)
{
if (MOD(n,2) == 0) // even
{
x = MOD(MULTIPLY(x,x),m);
n = DIVIDE(n,2);
}
else
{
result = MOD(MULTIPLY(result,x),m);
n = n-1;
}
} // while
return result;
} // expmod
Damn! We didn’t save all that much. But we did save 10% or so.
Note: We can make this a bit tighter.
Our break came before turning the TR version into an iterative one.
Things Sam hoped were reinforced (in addition to the above)
Speaking of big improvements: Are there constant-time ways to compute x^n, if we’re okay with approximations?
WAG: Find the biggest power of 2 that’s close and bit shift it. Whoops. That may be logarithmic.
Let’s assume we can find log_e(x) in constant time. Take a look at the math.
x^n = e^(log_e(x))^n = e^(log_e(x)*n)
If you can do e^k quickly and log_e quickly, you can approximate x^n quickly.
Before there were computers, you looked them up in books. I don’t know how log and e work right now.
Queues of strings.
Interface?
#ifndef __SQ_H__
#define __SQ_H__
// +------------------+----------------------------------------------
// | Type Definitions |
// +------------------+
typedef struct sq {
} sq;
// +-------------------+---------------------------------------------
// | Utility Functions |
// +-------------------+
/**
* Add str to the queue, provided we haven't reached the limit.
* If we reach the limit, returns 0. Otherwise, returns 1.
*
* We allocate space for a copy of the string.
*/
int sq_enqueue (sq *q, char *str);
/**
* Remove something from the queue, provided the queue is not empty.
*
* The client is responsible for freeing this string.
*/
char *sq_dequeue (sq *q);
/**
* Check if the queue is empty
*/
? sq_empty (?);
/**
* Check the front element (if the queue is nonempty)
*/
? sq_peek (?);
/**
* Determine the size of the queue
*/
? sq_size (?)
#endif __SQ_H__
Implementation: Your task for next class.
We’ll consider some issues and their effects.
Example
int *ip = (int *) malloc (sizeof (int));
int *jp = ip;
*ip = 5;
free (ip);
printf ("%d\n", *jp);
*ip = (int *) malloc (sizeof (int));
*ip = 42;
printf ("%d\n", *jp);
Effects?
Other ways to “use memory after freeing”?
Examples
Effects?
Examples
Effects?
Examples
Effects?
Examples
Effects?
Examples
Effects?
How do we diagnose these errors?
/**
* example.c
* An example for our discussions.
*/
// +---------+---------------------------------------------------------
// | Headers |
// +---------+
#include <assert.h>
#include <stdio.h>
// +---------+---------------------------------------------------------
// | Helpers |
// +---------+
/**
* Allocate an array.
*/
void
vals (int *result[])
{
int values[10];
*result = values;
} // vals
/**
* A function that fills in some values in an array.
*/
void
f (int n)
{
int i;
int values[10];
for (i = 0; i < 10; i++)
{
values[i] = n;
assert(values[i] == n);
} // for
} // f
// +------+------------------------------------------------------------
// | Main |
// +------+
int
main (void)
{
int *stuff;
vals (&stuff);
stuff[0] = 42;
f (5);
assert (42 = stuff[0]);
return 0;
} // main
#include <stdlib.h>
#include <stdio.h>
void
f (int *ip)
{
int i = *ip;
free (ip);
ip = malloc (sizeof (int));
*ip = i + 1;
}
int
main (int argc, char *argv[])
{
int *stuff;
stuff = malloc (sizeof (int));
*stuff = 5;
f (stuff);
assert (6 == *stuff);
*stuff = 1;
assert (1 == *stuff);
free (stuff);
return 0;
} // main
/**
* example.c
* An example for our discussion of lldb and such.
*/
// +---------+---------------------------------------------------------
// | Headers |
// +---------+
#include <assert.h>
#include <stdio.h>
// +---------+---------------------------------------------------------
// | Helpers |
// +---------+
/**
* Allocate an array.
*/
void
vals (int *result[])
{
int values[10];
*result = values;
} // vals
/**
* A function that fills in some values in an array.
*/
void
f (int n)
{
int i;
int values[10];
for (i = 0; i < 10; i++)
{
values[i] = n;
assert(values[i] == n);
} // for
} // f
// +------+------------------------------------------------------------
// | Main |
// +------+
int
main (void)
{
int *stuff;
vals (&stuff);
stuff[0] = 42;
f (5);
assert (stuff[0] == 42);
return 0;
} // main
For next class. Left here to remind me.