Related
Try to calculate:
by storing 1/N and X as float variable. Which result do you get for N=10000, 100000 and 1000000?
Now try to use double variables. Does it change outcome?
In order to do this I wrote this code:
#TRUNCATION ERRORS
import numpy as np #library for numerical calculations
import matplotlib.pyplot as plt #library for plotting purposes
x = 0
n = 10**6
X = []
N = []
for i in range(1,n+1):
x = x + 1/n
item = float(x)
item2 = float(n)
X.append(item)
N.append(item2)
plt.figure() #block for plot purpoes
plt.plot(N,X,marker=".")
plt.xlabel('N')
plt.ylabel('X')
plt.grid()
plt.show()
The output is:
This is wrong because the output should be like that (showed in the lecture):
First, you want to plot N on the x-axis, but you're actually plotting 1/N.
Second, you aren't calculating the expression you think you're calculating. It looks like you're calculating sum_{i=1..N}(1/i).
You need to calculate sum_{i=1..N}(1/N) which is 1/N + 1/N + ... + 1/N repeated N times. In other words, you want to calculate N * 1 / N, which should be equal to 1. Your exercise is showing you that it won't be when you use floating-point math, because reasons.
To do this correctly, let's first define a list of values for N
Nvals = [1, 10, 100, 1000, 10000, 100000, 1000000]
Let's define a function that will calculate our summation for a single value of N:
def calc_sum(N):
total = 0
for i in range(N):
total += 1 / N
return total
Next, let's create an empty list of Xvals and fill it up with the calculated sum for each N
Xvals = []
for N in Nvals:
Xvals.append(calc_sum(N))
or, as a listcomprehension:
Xvals = [calc_sum(N) for N in Nvals]
Now we get this value of Xvals:
[1.0,
0.9999999999999999,
1.0000000000000007,
1.0000000000000007,
0.9999999999999062,
0.9999999999980838,
1.000000000007918]
Clearly, they are not all equal to 1.
You can increase the number of values in Nvals to get a denser plot, but the idea is the same.
Now pay attention to what #khelwood said in their comment:
"float variables" and "double variables" are not a thing in Python. Variables don't have types. And floats are 64 bit
Python floats are all 64-bit floating-point numbers, so you can't do your exercise in python. If you used a language like C or C++ that actually has 32-bit float and 64-bit double types, you'd get something like this:
Try it online
#include <iostream>
float calc_sum_f(int N) {
float total = 0.0;
for (int i = 0; i < N; i++)
total += ((float)1 / N);
return total;
}
double calc_sum_d(int N) {
double total = 0.0;
for (int i = 0; i < N; i++)
total += ((double)1 / N);
return total;
}
int main()
{
int Nvals[7] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
std::cout << "N\tdouble\tfloat" << std::endl;
for (int ni = 0; ni < 7; ni++) {
int N = Nvals[ni];
double x_d = calc_sum_d(N);
float x_f = calc_sum_f(N);
std::cout << N << "\t" << x_d << "\t" << x_f << std::endl;
}
}
Output:
N double float
1 1 1
10 1 1
100 1 0.999999
1000 1 0.999991
10000 1 1.00005
100000 1 1.00099
1000000 1 1.00904
Here you can see that 32-bit floats don't have enough precision beyond a certain value of N to accurately calculate N * 1 / N. There's no reason the plot should look like your hand-drawn plot, because there's no reason it will decrease consistently as we can evidently see here.
Using numpy Thanks for the suggestion #Kelly to get 32-bit and 64-bit floating point types in python, we can similarly define two functions:
def calc_sum_64(N):
c = np.float64(0)
one_over_n = np.float64(1) / np.float64(N)
for i in range(N):
c += one_over_n
return c
def calc_sum_32(N):
c = np.float32(0)
one_over_n = np.float32(1) / np.float32(N)
for i in range(N):
c += one_over_n
return c
Then, we find Xvals_64 and Xvals_32
Nvals = [10**i for i in range(7)]
Xvals_32 = [calc_sum_32(N) for N in Nvals]
Xvals_64 = [calc_sum_64(N) for N in Nvals]
And we get:
Xvals_32 = [1.0, 1.0000001, 0.99999934, 0.9999907, 1.0000535, 1.0009902, 1.0090389]
Xvals_64 = [1.0,
0.9999999999999999,
1.0000000000000007,
1.0000000000000007,
0.9999999999999062,
0.9999999999980838,
1.000000000007918]
I haven't vectorized my numpy code to make it easier for you to understand what's going on, but Kelly shows a great way to vectorize it to speed up the calculation:
sum(1/N) from i = 1 to N is (1 / N) + (1 / N) + (1 / N) + ... {N times} , which is an array of N ones, divided by N and then summed. You could write the calc_sum_32 and calc_sum_64 functions like so:
def calc_sum_32(N):
return (np.ones((N,), dtype=np.float32) / np.float32(N)).sum()
def calc_sum_64(N):
return (np.ones((N,), dtype=np.float64) / np.float64(N)).sum()
You can then call these functions for every value of N you care about, and get a plot that looks like so, which shows the result oscillating about 1 for float32, but barely any oscillation for float64:
I am studying algorithms in Python and solving a question that is:
Let x(k) be a recursively defined string with base case x(1) = "123"
and x(k) is "1" + x(k-1) + "2" + x(k-1) + "3". Given three positive
integers k,s, and t, find the substring x(k)[s:t].
For example, if k = 2, s = 1 and t = 5,x(2) = 112321233 and x(2)[1:5]
= 1232.
I have solved it using a simple recursive function:
def generate_string(k):
if k == 1:
return "123"
part = generate_string(k -1)
return ("1" + part + "2" + part + "3")
print(generate_string(k)[s,t])
Although my first approach gives correct answer, the problem is that it takes too long to build string x when k is greater than 20. The program need to be finished within 16 seconds while k is below 50. I have tried to use memoization but it does not help as I am not allowed to cache each test case. I thus think that I must avoid using recursive function to speed up the program. Is there any approaches I should consider?
We can see that the string represented by x(k) grows exponentially in length with increasing k:
len(x(1)) == 3
len(x(k)) == len(x(k-1)) * 2 + 3
So:
len(x(k)) == 3 * (2**k - 1)
For k equal to 100, this amounts to a length of more than 1030. That's more characters than there are atoms in a human body!
Since the parameters s and t will take (in comparison) a tiny, tiny slice of that, you should not need to produce the whole string. You can still use recursion though, but keep passing an s and t range to each call. Then when you see that this slice will actually be outside of the string you would generate, then you can just exit without recursing deeper, saving a lot of time and (string) space.
Here is how you could do it:
def getslice(k, s, t):
def recur(xsize, s, t):
if xsize == 0 or s >= xsize or t <= 0:
return ""
smaller = (xsize - 3) // 2
return ( ("1" if s <= 0 else "")
+ recur(smaller, s-1, t-1)
+ ("2" if s <= smaller+1 < t else "")
+ recur(smaller, s-smaller-2, t-smaller-2)
+ ("3" if t >= xsize else "") )
return recur(3 * (2**k - 1), s, t)
This doesn't use any caching of x(k) results... In my tests this was fast enough.
Based on #FMc's answer, here's some python3 code that calculates x(k, s, t):
from functools import lru_cache
from typing import *
def f_len(k) -> int:
return 3 * ((2 ** k) - 1)
#lru_cache(None)
def f(k) -> str:
if k == 1:
return "123"
return "1" + f(k - 1) + "2" + f(k - 1) + "3"
def substring_(k, s, t, output) -> None:
# Empty substring.
if s >= t or k == 0:
return
# (An optimization):
# If all the characters need to be included, just calculate the string and cache it.
if s == 0 and t == f_len(k):
output.append(f(k))
return
if s == 0:
output.append("1")
sub_len = f_len(k - 1)
substring_(k - 1, max(0, s - 1), min(sub_len, t - 1), output)
if s <= 1 + sub_len < t:
output.append("2")
substring_(k - 1, max(0, s - sub_len - 2), min(sub_len, t - sub_len - 2), output)
if s <= 2 * (1 + sub_len) < t:
output.append("3")
def substring(k, s, t) -> str:
output: List[str] = []
substring_(k, s, t, output)
return "".join(output)
def test(k, s, t) -> bool:
actual = substring(k, s, t)
expected = f(k)[s:t]
return actual == expected
assert test(1, 0, 3)
assert test(2, 2, 6)
assert test(2, 1, 5)
assert test(2, 0, f_len(2))
assert test(3, 0, f_len(3))
assert test(8, 44, 89)
assert test(10, 1001, 2022)
assert test(14, 12345, 45678)
assert test(17, 12345, 112345)
# print(substring(30, 10000, 10100))
print("Tests passed")
This is an interesting problem. I'm not sure whether I'll have time to write the code, but here's an outline of how you can solve it. Note: see the better answer from trincot.
As discussed in the comments, you cannot generate the actual string: you will quickly run out of memory as k grows. But you can easily compute the length of that string.
First some notation:
f(k) : The generated string.
n(k) : The length of f(k).
nk1 : n(k-1), which is used several times in table below.
For discussion purposes, we can divide the string into the following regions. The start/end values use standard Python slice numbering:
Region | Start | End | Len | Subtring | Ex: k = 2
-------------------------------------------------------------------
A | 0 | 1 | 1 | 1 | 0:1 1
B | 1 | 1 + nk1 | nk1 | f(k-1) | 1:4 123
C | 1 + nk1 | 2 + nk1 | 1 | 2 | 4:5 2
D | 2 + nk1 | 2 + nk1 + nk1 | nk1 | f(k-1) | 5:8 123
E | 2 + nk1 + nk1 | 3 + nk1 + nk1 | 1 | 3 | 8:9 3
Given k, s, and t we need to figure out which region of the string is relevant. Take a small example:
k=2, s=6, and t=8.
The substring defined by 6:8 does not require the full f(k). We only need
region D, so we can turn our attention to f(k-1).
To make the shift from k=2 to k=1, we need to adjust s and t: specifically,
we need to subtract the total length of regions A + B + C. For k=2, that
length is 5 (1 + nk1 + 1).
Now we are dealing with: k=1, s=1, and t=3.
Repeat as needed.
Whenever k gets small enough, we stop this nonsense and actually generate the string so we can grab the needed substring directly.
It's possible that some values of s and t could cross region boundaries. In that case, divide the problem into two subparts (one for each region needed). But the general idea is the same.
Here's a commented iterative version in JavaScript that's very easy to convert to Python.
In addition to being what you asked for, that is non-recursive, it allows us to solve things like f(10000, 10000, 10050), which seem to exceed Python default recursion depth.
// Generates the full string
function g(k){
if (k == 1)
return "123";
prev = g(k - 1);
return "1" + prev + "2" + prev + "3";
}
function size(k){
return 3 * ((1 << k) - 1);
}
// Given a depth and index,
// we'd like (1) a string to
// output, (2) the possible next
// part of the same depth to
// push to the stack, and (3)
// possibly the current section
// mapped deeper to also push to
// the stack. (2) and (3) can be
// in a single list.
function getParams(depth, i){
const psize = size(depth - 1);
if (i == 0){
return ["1", [[depth, 1 + psize], [depth - 1, 0]]];
} else if (i < 1 + psize){
return ["", [[depth, 1 + psize], [depth - 1, i - 1]]];
} else if (i == 1 + psize){
return ["2", [[depth, 2 + 2 * psize], [depth - 1, 0]]];
} else if (i < 2 + 2 * psize){
return ["", [[depth, 2 + 2 * psize], [depth - 1, i - 2 - psize]]];
} else {
return ["3", []];
}
}
function f(k, s, t){
let len = t - s;
let str = "";
let stack = [[k, s]];
while (str.length < len){
const [depth, i] = stack.pop();
if (depth == 1){
const toTake = Math.min(3 - i, len - str.length);
str = str + "123".substr(i, toTake);
} else {
const [s, rest] = getParams(depth, i);
str = str + s;
stack.push(...rest);
}
}
return str;
}
function test(k, s, t){
const l = g(k).substring(s, t);
const r = f(k, s, t);
console.log(g(k).length);
//console.log(g(k))
console.log(l);
console.log(r);
console.log(l == r);
}
test(1, 0, 3);
test(2, 2, 6);
test(2, 1, 5);
test(4, 44, 45);
test(5, 30, 40);
test(7, 100, 150);
I am working through a Geekforgeeks practice question. I have come up with a naive recursive solution to the "maximum tip calculator" problem.
The problem definition is:
Restaurant recieves N orders. If Rahul takes the ith order, gain
$A[i]. If Ankit takes this order, the tip would be $B[i] One order
per person. Rahul takes max X orders. Ankit takes max Y orders.
X + Y >= N. Find out the maximum possible amount of total tip money
after processing all the orders.
Input:
The first line contains one integer, number of test cases. The second
line contains three integers N, X, Y. The third line contains N
integers. The ith integer represents Ai. The fourth line contains N
integers. The ith integer represents Bi.
Output: Print a single integer representing the maximum tip money they
would receive.
My Code and working sample:
def max_tip(N, A, B, X, Y, n= 0):
if n == len(A) or N == 0:
return 0
if X == 0 and Y > 0: # rahul cannot take more orders
return max(B[n] + max_tip(N - 1, A, B, X, Y - 1, n + 1), # ankit takes the order
max_tip(N, A, B, X, Y, n + 1)) # ankit does not take order
elif Y == 0 and X > 0: # ankit cannot take more orders
return max(A[n] + max_tip(N - 1, A, B, X - 1, Y, n + 1), # rahul takes the order
max_tip(N, A, B, X, Y, n + 1)) # rahul does not take order
elif Y == 0 and X == 0: # neither can take orders
return 0
else:
return max(A[n] + max_tip(N - 1, A, B, X - 1, Y, n + 1), # rahul takes the order
B[n] + max_tip(N - 1, A, B, X, Y - 1, n + 1), #ankit takes the order
max_tip(N, A, B, X, Y, n + 1)) # nobody takes the order
T = int(input())
for i in range(T):
nxy = [int(n) for n in input().strip().split(" ")]
N = nxy[0]
X = nxy[1]
Y = nxy[2]
A = [int(n) for n in input().strip().split(" ")]
B = [int(n) for n in input().strip().split(" ")]
print(max_tip(N, A, B, X, Y))
I've annotated my recursive call decisions. Essentially I extended the naive solution for 0-1 knapsack in another dimension two waiters, either one takes, the other takes, or both do not take the order depending on the orders left constraint.
The solution checker is complaining for the following testcase:
Input:
7 3 3
8 7 15 19 16 16 18
1 7 15 11 12 31 9
Its Correct output is:
110
And Your Code's Output is:
106
This confuses me because the optimal solution seems to be what my code is getting (19 + 16 + 18) + (7 + 15 + 31). The immediate issue seems to be that X + Y < N. My thought is my code should work for the case where X + Y < N as well.
What's going on?
you are considering the case, where nobody takes the tip. But that case doesn't exist as X+Y >= n. This java code worked for me, have a look.
private static int getMaxTip(int x, int y, int n, int[] A, int[] B) {
int[][] dp = new int[x + 1][y + 1];
dp[0][0] = 0;
for (int i = 1;i <= x;i++) {
dp[i][0] = (i <= n) ? dp[i - 1][0] + A[i - 1] : dp[i - 1][0];
}
for (int i = 1;i <= y;i++) {
dp[0][i] = (i <= n) ? dp[0][i - 1] + B[i - 1] : dp[0][i - 1];
}
for (int i = 1;i <= x;i++) {
for (int j = 1;j <= y;j++) {
if (i + j <= n) {
dp[i][j] = Math.max(dp[i - 1][j] + A[i + j - 1], dp[i][j - 1] + B[i + j - 1]);
}
}
}
int ans = Integer.MIN_VALUE;
for (int i = 0;i <= x;i++) {
for (int j = 0;j <= y;j++) {
if (i + j == n) {
ans = Math.max(ans, dp[i][j]);
}
}
}
return ans;
}
You are considering a case when nobody takes the order that should not be considered as it is mentioned in the question that x+y>=n always.Removing that condition will work.
I am assuming, this is your source of question:
https://practice.geeksforgeeks.org/problems/maximum-tip-calculator/0
Here is my solution written in Python that passed all case:
https://github.com/Madhu-Guddana/My-Solutions/blob/master/adhoc/max_tip.py
Explanation:
zip corresponding element of tips and create new array.
Sort the new array based on difference amount value for Rahul and Ankit,
Then we can safely consider the elements from 2 ends of the array, which ever end is giving more profit, add the value to count.
I'm having some trouble getting the correct solution for the following problem:
Your goal is given a positive integer n, find the minimum number of
operations needed to obtain the number n starting from the number 1.
More specifically the test case I have in the comments below.
# Failed case #3/16: (Wrong answer)
# got: 15 expected: 14
# Input:
# 96234
#
# Your output:
# 15
# 1 2 4 5 10 11 22 66 198 594 1782 5346 16038 16039 32078 96234
# Correct output:
# 14
# 1 3 9 10 11 22 66 198 594 1782 5346 16038 16039 32078 96234
# (Time used: 0.10/5.50, memory used: 8601600/134217728.)
def optimal_sequence(n):
sequence = []
while n >= 1:
sequence.append(n)
if n % 3 == 0:
n = n // 3
optimal_sequence(n)
elif n % 2 == 0:
n = n // 2
optimal_sequence(n)
else:
n = n - 1
optimal_sequence(n)
return reversed(sequence)
input = sys.stdin.read()
n = int(input)
sequence = list(optimal_sequence(n))
print(len(sequence) - 1)
for x in sequence:
print(x, end=' ')
It looks like I should be outputting 9 where I'm outputting 4 & 5 but I'm not sure why this isn't the case. What's the best way to troubleshoot this problem?
You are doing a greedy approach.
When n == 10, you check and see if it's divisible by 2 assuming that's the best step, which is wrong in this case.
What you need to do is proper dynamic programming. v[x] will hold the minimum number of steps to get to result x.
def solve(n):
v = [0]*(n+1) # so that v[n] is there
v[1] = 1 # length of the sequence to 1 is 1
for i in range(1,n):
if not v[i]: continue
if v[i+1] == 0 or v[i+1] > v[i] + 1: v[i+1] = v[i] + 1
# Similar for i*2 and i*3
solution = []
while n > 1:
solution.append(n)
if v[n-1] == v[n] - 1: n = n-1
if n%2 == 0 and v[n//2] == v[n] -1: n = n//2
# Likewise for n//3
solution.append(1)
return reverse(solution)
One more solution:
private static List<Integer> optimal_sequence(int n) {
List<Integer> sequence = new ArrayList<>();
int[] arr = new int[n + 1];
for (int i = 1; i < arr.length; i++) {
arr[i] = arr[i - 1] + 1;
if (i % 2 == 0) arr[i] = Math.min(1 + arr[i / 2], arr[i]);
if (i % 3 == 0) arr[i] = Math.min(1 + arr[i / 3], arr[i]);
}
for (int i = n; i > 1; ) {
sequence.add(i);
if (arr[i - 1] == arr[i] - 1)
i = i - 1;
else if (i % 2 == 0 && (arr[i / 2] == arr[i] - 1))
i = i / 2;
else if (i % 3 == 0 && (arr[i / 3] == arr[i] - 1))
i = i / 3;
}
sequence.add(1);
Collections.reverse(sequence);
return sequence;
}
List<Integer> sequence = new ArrayList<Integer>();
while (n>0) {
sequence.add(n);
if (n % 3 == 0&&n % 2 == 0)
n=n/3;
else if(n%3==0)
n=n/3;
else if (n % 2 == 0&& n!=10)
n=n/2;
else
n=n-1;
}
Collections.reverse(sequence);
return sequence;
Here's my Dynamic programming (bottom-up & memoized)solution to the problem:
public class PrimitiveCalculator {
1. public int minOperations(int n){
2. int[] M = new int[n+1];
3. M[1] = 0; M[2] = 1; M[3] = 1;
4. for(int i = 4; i <= n; i++){
5. M[i] = M[i-1] + 1;
6. M[i] = Math.min(M[i], (i %3 == 0 ? M[i/3] + 1 : (i%3 == 1 ? M[(i-1)/3] + 2 : M[(i-2)/3] + 3)));
7. M[i] = Math.min(M[i], i%2 == 0 ? M[i/2] + 1: M[(i-1)/2] + 2);
8. }
9. return M[n];
10. }
public static void main(String[] args) {
System.out.println(new PrimitiveCalculator().minOperations(96234));
}
}
Before going ahead with the explanation of the algorithm I would like to add a quick disclaimer:
A DP solution is not reached at first attempt unless you have good
experience solving lot of DP problems.
Approach to solving through DP
If you are not comfortable with DP problems then the best approach to solve the problem would be following:
Try to get a working brute-force recursive solution.
Once we have a recursive solution, we can look for ways to reduce the recursive step by adding memoization, where in we try remember the solution to the subproblems of smaller size already solved in a recursive step - This is generally a top-down solution.
After memoization, we try to flip the solution around and solve it bottom up (my Java solution above is a bottom-up one)
Once you have done above 3 steps, you have reached a DP solution.
Now coming to the explanation of the solution above:
Given a number 'n' and given only 3 operations {+1, x2, x3}, the minimum number of operations needed to reach to 'n' from 1 is given by recursive formula:
min_operations_to_reach(n) = Math.min(min_operations_to_reach(n-1), min_operations_to_reach(n/2), min_operations_to_reach(n/3))
If we flip up the memoization process and begin with number 1 itself then the above code starts to make better sense.
Starting of with trivial cases of 1, 2, 3
min_operations_to_reach(1) = 0 because we dont need to do any operation.
min_operations_to_reach(2) = 1 because we can either do (1 +1) or (1 x2), in either case number of operations is 1.
Similarly, min_operations_to_reach(3) = 1 because we can multiply 1 by 3 which is one operation.
Now taking any number x > 3, the min_operations_to_reach(x) is the minimum of following 3:
min_operations_to_reach(x-1) + 1 because whatever is the minimum operations to reach (x-1) we can add 1 to it to get the operation count to make it number x.
Or, if we consider making number x from 1 using multiplication by 3 then we have to consider following 3 options:
If x is divisible by 3 then min_operations_to_reach(x/3) + 1,
if x is not divisible by 3 then x%3 can be 1, in which case its min_operations_to_reach((x-1)/3) + 2, +2 because one operation is needed to multiply by 3 and another operation is needed to add 1 to make the number 'x'
Similarly if x%3 == 2, then the value will be min_operations_to_reach((x-2)/3) + 3. +3 because 1 operation to multiply by 3 and then add two 1s subsequently to make the number x.
Or, if we consider making number x from 1 using multiplication by 2 then we have to consider following 2 options:
if x is divisible by 2 then its min_operations_to_reach(x/2) + 1
if x%2 == 1 then its min_operations_to_reach((x-1)/2) + 2.
Taking the minimum of above 3 we can get the minimum number of operations to reach x. Thats what is done in code above in lines 5, 6 and 7.
def DPoptimal_sequence(n,operations):
MinNumOperations=[0]
l_no=[]
l_no2=[]
for i in range(1,n+1):
MinNumOperations.append(None)
for operation in operations:
if operation==1:
NumOperations=MinNumOperations[i-1]+1
if operation==2 and i%2==0:
NumOperations=MinNumOperations[int(i/2)]+1
if operation==3 and i%3==0:
NumOperations=MinNumOperations[int(i/3)]+1
if MinNumOperations[i]==None:
MinNumOperations[i]=NumOperations
elif NumOperations<MinNumOperations[i]:
MinNumOperations[i]=NumOperations
if MinNumOperations[i] == MinNumOperations[i-1]+1:
l_no2.append((i,i-1))
elif MinNumOperations[i] == MinNumOperations[int(i/2)]+1 and i%2 == 0:
l_no2.append((i,int(i/2)))
elif MinNumOperations[i] == MinNumOperations[int(i/3)]+1 and i%3 == 0:
l_no2.append((i,int(i/3)))
l_no.append((i,MinNumOperations[i]-1))
#print(l_no)
#print(l_no2)
x=MinNumOperations[n]-1
#print('x',x)
l_no3=[n]
while n>1:
a,b = l_no2[n-1]
#print(a,b)
if b == a-1:
n = n-1
#print('1111111111111')
#print('n',n)
l_no3.append(n)
elif b == int(a/2) and a%2==0:
n = int(n/2)
#print('22222222222222222')
#print('n',n)
l_no3.append(n)
elif b == int(a/3) and a%3==0:
n = int(n/3)
#print('333333333333333')
#print('n',n)
l_no3.append(n)
#print(l_no3)
return x,l_no3
def optimal_sequence(n):
hop_count = [0] * (n + 1)
hop_count[1] = 1
for i in range(2, n + 1):
indices = [i - 1]
if i % 2 == 0:
indices.append(i // 2)
if i % 3 == 0:
indices.append(i // 3)
min_hops = min([hop_count[x] for x in indices])
hop_count[i] = min_hops + 1
ptr = n
optimal_seq = [ptr]
while ptr != 1:
candidates = [ptr - 1]
if ptr % 2 == 0:
candidates.append(ptr // 2)
if ptr % 3 == 0:
candidates.append(ptr // 3)
ptr = min(
[(c, hop_count[c]) for c in candidates],
key=lambda x: x[1]
)[0]
optimal_seq.append(ptr)
return reversed(optimal_seq)
private int count(int n, Map<Integer, Integer> lookup) {
if(lookup.containsKey(n)) {
return lookup.get(n);
}
if(n==1) {
return 0;
} else {
int result;
if(n%2==0 && n%3==0) {
result =1+
//Math.min(count(n-1, lookup),
Math.min(count(n/2, lookup),
count(n/3, lookup));
} else if(n%2==0) {
result = 1+ Math.min(count(n-1, lookup),
count(n/2, lookup));
} else if(n%3==0) {
result = 1+ Math.min(count(n-1, lookup), count(n/3, lookup));
} else {
result = 1+ count(n-1, lookup);
}
//System.out.println(result);
lookup.put(n, result);
return result;
}
}
Been trying to implement Rabin-Miller Strong Pseudoprime Test today.
Have used Wolfram Mathworld as reference, lines 3-5 sums up my code pretty much.
However, when I run the program, it says (sometimes) that primes (even low such as 5, 7, 11) are not primes. I've looked over the code for a very long while and cannot figure out what is wrong.
For help I've looked at this site aswell as many other sites but most use another definition (probably the same, but since I'm new to this kind of math, I can't see the same obvious connection).
My Code:
import random
def RabinMiller(n, k):
# obviously not prime
if n < 2 or n % 2 == 0:
return False
# special case
if n == 2:
return True
s = 0
r = n - 1
# factor n - 1 as 2^(r)*s
while r % 2 == 0:
s = s + 1
r = r // 2 # floor
# k = accuracy
for i in range(k):
a = random.randrange(1, n)
# a^(s) mod n = 1?
if pow(a, s, n) == 1:
return True
# a^(2^(j) * s) mod n = -1 mod n?
for j in range(r):
if pow(a, 2**j*s, n) == -1 % n:
return True
return False
print(RabinMiller(7, 5))
How does this differ from the definition given at Mathworld?
1. Comments on your code
A number of the points I'll make below were noted in other answers, but it seems useful to have them all together.
In the section
s = 0
r = n - 1
# factor n - 1 as 2^(r)*s
while r % 2 == 0:
s = s + 1
r = r // 2 # floor
you've got the roles of r and s swapped: you've actually factored n − 1 as 2sr. If you want to stick to the MathWorld notation, then you'll have to swap r and s in this section of the code:
# factor n - 1 as 2^(r)*s, where s is odd.
r, s = 0, n - 1
while s % 2 == 0:
r += 1
s //= 2
In the line
for i in range(k):
the variable i is unused: it's conventional to name such variables _.
You pick a random base between 1 and n − 1 inclusive:
a = random.randrange(1, n)
This is what it says in the MathWorld article, but that article is written from the mathematician's point of view. In fact it is useless to pick the base 1, since 1s = 1 (mod n) and you'll waste a trial. Similarly, it's useless to pick the base n − 1, since s is odd and so (n − 1)s = −1 (mod n). Mathematicians don't have to worry about wasted trials, but programmers do, so write instead:
a = random.randrange(2, n - 1)
(n needs to be at least 4 for this optimization to work, but we can easily arrange that by returning True at the top of the function when n = 3, just as you do for n = 2.)
As noted in other replies, you've misunderstood the MathWorld article. When it says that "n passes the test" it means that "n passes the test for the base a". The distinguishing fact about primes is that they pass the test for all bases. So when you find that as = 1 (mod n), what you should do is to go round the loop and pick the next base to test against.
# a^(s) = 1 (mod n)?
x = pow(a, s, n)
if x == 1:
continue
There's an opportunity for optimization here. The value x that we've just computed is a20 s (mod n). So we could test it immediately and save ourselves one loop iteration:
# a^(s) = ±1 (mod n)?
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
In the section where you calculate a2j s (mod n) each of these numbers is the square of the previous number (modulo n). It's wasteful to calculate each from scratch when you could just square the previous value. So you should write this loop as:
# a^(2^(j) * s) = -1 (mod n)?
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
It's a good idea to test for divisibility by small primes before trying Miller–Rabin. For example, in Rabin's 1977 paper he says:
In implementing the algorithm we incorporate some laborsaving steps. First we test for divisibility by any prime p < N, where, say N = 1000.
2. Revised code
Putting all this together:
from random import randrange
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] # etc.
def probably_prime(n, k):
"""Return True if n passes k rounds of the Miller-Rabin primality
test (and is probably prime). Return False if n is proved to be
composite.
"""
if n < 2: return False
for p in small_primes:
if n < p * p: return True
if n % p == 0: return False
r, s = 0, n - 1
while s % 2 == 0:
r += 1
s //= 2
for _ in range(k):
a = randrange(2, n - 1)
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
In addition to what Omri Barel has said, there is also a problem with your for loop. You will return true if you find one a that passes the test. However, all a have to pass the test for n to be a probable prime.
I'm wondering about this piece of code:
# factor n - 1 as 2^(r)*s
while r % 2 == 0:
s = s + 1
r = r // 2 # floor
Let's take n = 7. So n - 1 = 6. We can express n - 1 as 2^1 * 3. In this case r = 1 and s = 3.
But the code above finds something else. It starts with r = 6, so r % 2 == 0. Initially, s = 0 so after one iteration we have s = 1 and r = 3. But now r % 2 != 0 and the loop terminates.
We end up with s = 1 and r = 3 which is clearly incorrect: 2^r * s = 8.
You should not update s in the loop. Instead, you should count how many times you can divide by 2 (this will be r) and the result after the divisions will be s. In the example of n = 7, n - 1 = 6, we can divide it once (so r = 1) and after the division we end up with 3 (so s = 3).
Here's my version:
# miller-rabin pseudoprimality checker
from random import randrange
def isStrongPseudoprime(n, a):
d, s = n-1, 0
while d % 2 == 0:
d, s = d/2, s+1
t = pow(a, d, n)
if t == 1:
return True
while s > 0:
if t == n - 1:
return True
t, s = pow(t, 2, n), s - 1
return False
def isPrime(n, k):
if n % 2 == 0:
return n == 2
for i in range(1, k):
a = randrange(2, n)
if not isStrongPseudoprime(n, a):
return False
return True
If you want to know more about programming with prime numbers, I modestly recommend this essay on my blog.
You should also have a look at Wikipedia, where known "random" sequences gives guaranteed answers up to a given prime.
if n < 1,373,653, it is enough to test a = 2 and 3;
if n < 9,080,191, it is enough to test a = 31 and 73;
if n < 4,759,123,141, it is enough to test a = 2, 7, and 61;
if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11;
if n < 3,474,749,660,383, it is enough to test a = 2, 3, 5, 7, 11, and 13;
if n < 341,550,071,728,321, it is enough to test a = 2, 3, 5, 7, 11, 13, and 17;