Skip to content

Instantly share code, notes, and snippets.

@mutaku
Created November 15, 2012 21:23
Show Gist options
  • Save mutaku/4081368 to your computer and use it in GitHub Desktop.
Save mutaku/4081368 to your computer and use it in GitHub Desktop.
palindromes (output stuffs here: https://gist.github.com/4219658 )
/* palindrome.cpp
* C++ implementation of palindrome.py
* (https://gist.github.com/4081368)
*
* - Matthew Martz 2012
*/
#include <iostream>
#include <string>
#include <sstream>
#include <cmath>
#include <gmp.h>
#include <time.h>
// includes for testing only
#include <typeinfo>
using namespace std;
struct Factors {
long long i;
long long j;
};
Factors factorize(long long product){
// square sieving to search for factors
long long symmetry;
symmetry = floor(sqrt(product) + 0.5);
int mag;
mag = floor(log10(product)) + 1;
long long factor_ceiling, factor_floor;
factor_ceiling = pow((double)10, (mag / 2)) - 1;
factor_floor = symmetry - (factor_ceiling - symmetry);
cout << "floor : " << factor_floor << " ceiling : " << factor_ceiling << endl;
Factors results = { symmetry, symmetry };
long long low_high_product;
if (pow((double)factor_ceiling, 2) < product){
results.i = 0;
results.j = 0;
} else if ((factor_floor * factor_ceiling) == product){
results.i = factor_floor;
results.j = factor_ceiling;
} else {
while(1){
cout << product << endl;
cout << results.i << " , " << results.j << endl;
low_high_product = results.i * results.j;
if (low_high_product == product){
cout << "match" << endl;
break;
} else if (results.j < factor_floor ||
results.i < factor_floor){
cout << "too low" << endl;
results.i = 0;
results.j = 0;
break;
} else if (results.j > factor_ceiling ||
results.i > factor_ceiling){
cout << "too high" << endl;
results.i = 0;
results.j = 0;
break;
} else if (low_high_product < product){
cout << "up" << endl;
results.j++;
} else if (low_high_product > product){
cout << "down" << endl;
results.i--;
}
}
}
return results;
}
int get_digits(){
// retrieve digits for factor size from user
int number_digits;
cout << " Enter number of digits: " << endl;
cin >> number_digits;
return number_digits;
}
string as_string(long long number){
// convert integer to string
stringstream ss;
ss << number;
return ss.str();
}
long long as_int(string number){
// convert string number to int number
return atoi(number.c_str());
}
int main(int argc, char * argv[]){
clock_t start_time, end_time;
// get number of digits for factor size
int factor_size;
if (argc > 1){
factor_size = atoi(argv[1]);
} else {
factor_size = get_digits();
}
// product magnitude
int magnitude = factor_size * 2;
// maximum, or start iteration value
long long start = pow((double)10, magnitude) - 1;
// stop point for n by n digit factor products
// we should not hit this else we have no
// palindromes
long long stop = pow((double)10, (factor_size - 1));
// Cut the number into symmetrical elements
// we will then flip to make palindromes
// convert number into string and take first half substring
string number_string = as_string(start);
string symmetry_string = number_string.substr(0, factor_size);
long long symmetry = as_int(symmetry_string);
// ------------------------------------------------------------
// debugging things and making sure our conversions are working
//cout << symmetry << " - symmetry" << endl;
//cout << "start " << start << " - stop " << stop << endl;
//long long test_int = 5;
//string test_str = "5";
//cout << typeid(symmetry).name() << typeid(test_int).name() << endl;
//cout << typeid(symmetry_string).name() << typeid(test_str).name() << endl;
// end debugging stuffs
// ------------------------------------------------------------
start_time = clock();
long long max_pali;
long long num;
Factors results;
while (symmetry >= stop){
symmetry_string = as_string(symmetry);
num = as_int(symmetry_string +
string (symmetry_string.rbegin(),
symmetry_string.rend()));
// check this palindrome now
results = factorize(num);
if (results.i != 0 &&
results.j != 0){
cout << "found" << endl;
break;
}
// finally, increment our counters down
symmetry = as_int(symmetry_string);
symmetry--;
}
end_time = clock();
float seconds = float(end_time - start_time) / CLOCKS_PER_SEC;
cout << results.i << " , " << results.j << " , " << num << endl;
cout << seconds << " s to run" << endl;
return 0;
}
#########################################
# Find the maximum numerical palindrome
# product from multiplying together two
# factors of a certain magnitude
#
# e.g. What is the maximum palindromic
# product for 4digit x 4digit?
#
# >>> palindrome.get_pali(4)
# ((9999, 9901), '99000099')
#
# So the maximum palindromic product for
# 4digit*4digit is 99000099 yielded by
# 9999*9901.
#
# - Matthew Martz 2012
#########################################
from math import sqrt, log10
def get_pali(digits, limit_digits=True, square_sieve=True):
'''
Finds the maximum numerical palindrome for
n-digits*n-digits.
'''
max_pali = None
mag = digits * 2 # magnitude of product
start = 10**mag - 1
counter = start
# Cut the number into symmetrical elements
# we will then flip to make palindromes
symmetry = int(str(start)[:mag / 2])
# We set limit_digits if we want to ensure
# factors have to have no less than n digits
if limit_digits:
stop = 10**(mag - 1)
else:
stop = 0
# assign our desired factorization function
# sqrt is fastest and will find only n-digit factors
# the other version is slower but can potentially
# find all factors as well as any primes
if square_sieve:
factorization = factors_sqrt
else:
factorization = factors
while counter >= stop:
# If we have even magnitude our sequence
# is symmetrical and we just flip
if mag % 2 == 0:
# make palindrome by flipping first half
# and joining together with string slicing
# and then convert back to integer
num = int(str(symmetry)+str(symmetry)[::-1])
# check to see if we can make this
# from our possible factors of n digits
to_make = factorization(num)
if to_make != (0, 0):
max_pali = (to_make, num)
# we've found a possible palindrome
# since we are working top down, we
# know this is the maximum possible
# so we break out of the loop
break
else:
# since we are not symmetrical, we have a
# digit in the middle that can change and
# we need to iterate over it's 10 possible
# values working down from 9 to 0 with each
# part of the first half symmetry we test
for i in range(9, -1, -1):
num = int(str(symmetry)+str(i)+str(symmetry)[::-1])
# check to see if we can make this
# from our possible factors of n digits
to_make = factorization(num)
if to_make != (0, 0):
max_pali = (to_make, num)
# we've found a possible palindrome
# since we are working top down, we
# know this is the maximum possible
# so we break out of the loop
break
# increment down the couter so that we can stop
# when we hit our stop point we've set by digits
counter -= 1
# increment down the first half symmetry component
# that we are generating the palindromes with
# 9889 has symmetry of 98 and will now be 97 to
# give us a palindrome next loop of 9779
symmetry -= 1
# return our palindrome if we have it in the form of
# ((factor high, factor low), palindrome)
return max_pali
def factors(target, full_range=False, inclusive=False):
'''
Uses a squeeze algorithm to find factors
that may yield a given product. We are not
identifying multiple possible factor sets, we
only car if we can find at least one set to
yield the target product.
'''
# set our max and minimum values
# e.g. 3 digit factors would be
# 999-100 based on mag=3
# we can set full range to be all inclusive
# from 0 to mag to use this as a more generic
# factorization
mag = len(str(target)) / 2
if full_range:
start_low = 0
start_high = target
else:
start_low = 10**(mag - 1)
start_high = 10**mag - 1
# get symmetry (half way point) of our
# target product
symmetry = start_low * 2
# set our squeeze factors to the
# high and low start points
high, low = start_high, start_low
# setup a container for all the possible
# factors if we want inclusivity
if inclusive:
factor_list = []
while 1:
# if our target is greater than the square
# of the highest possible factor, we cannot
# make this product and break
if high**2 < target:
high, low = 0, 0
break
# if high and low iterators hit the
# symmetry point, we can't make product
# and going further is operating on factors
# we've already tried with opposite
# symmetry so we break
elif high <= symmetry and low >= symmetry:
high, low = 0, 0
break
# if we are less than the product, we need
# to increase the low squeeze factor
elif high * low < target:
low += 1
# if we are greater than the product, we
# need to decrease the high squeeze factor
elif high * low > target:
high -= 1
# if we've hit the target, break
elif high * low == target:
if inclusive:
factor_list.append((high, low))
high -= 1
low += 1
else:
break
# return our squeeze factors
if inclusive:
return factor_list
return (high, low)
def factors_sqrt(target):
'''
Uses sqrt sieving to find factors
of target
'''
# find our sqrt symmetry point and
# since we want integers we round and
# use the integer form of this value
symmetry = int(round(sqrt(target)))
# get the number of digits of the product
mag = int(log10(target) + 1)
# calculate the maximum and minimum
# n digit factor which is based on
# mag / 2 since we want n-digit * n-digit
# palindromes
factor_ceiling = 10**(mag / 2) - 1
#factor_floor = 10**(mag / 2 - 1)
# make the floor based on our symmetry point
# distance of maximum from this point
factor_floor = symmetry - (factor_ceiling - symmetry)
high, low = symmetry, symmetry
# break and set to 0, 0 if we cannot
# possibly make this product with n*n digits
if factor_ceiling**2 < target:
high, low = 0, 0
else:
while 1:
# if either iterator goes below floor
# we break
if high < factor_floor or low < factor_floor:
high, low = 0, 0
break
# if either iterator goes above ceiling
# we break
elif high > factor_ceiling or low > factor_ceiling:
high, low = 0, 0
break
# if we are too low, increment up an iterator
elif high * low < target:
low += 1
# if we are too high, increment down an iterator
elif high * low > target:
high -= 1
# if we've hit our target, we break with iterators
# resting at our factors
elif high * low == target:
break
# return our fast sieve products
return (high, low)
@esteigler
Copy link

Code (test.py):

import time
import palindrome

t0 = time.clock()
print palindrome.get_pali(8)
t = time.clock() - t0
print t

Python:

DTSN-02947-Eriks-iMac ~  $ python test.py
((99990001, 99999999), 9999000000009999)
22.074752

PyPy:

DTSN-02947-Eriks-iMac ~  $ Downloads/pypy-1.9/bin/pypy test.py
((99990001, 99999999), 9999000000009999)
0.298731

@esteigler
Copy link

Another datapoint (get_pali(10)):

((9999986701, 9999996699), 99999834000043899999L)
221.705623

@mutaku
Copy link
Author

mutaku commented Nov 27, 2012

Moved the calculation of max (factor_ceiling**2) outside of while statement since this won't change and we should only be doing it once, else go into the while loop:

    # break and set to 0, 0 if we cannot
    # possibly make this product with n*n digits
    if factor_ceiling**2 < target:
        high, low = 0, 0
    else:
        while 1:
            ...

@mutaku
Copy link
Author

mutaku commented Dec 5, 2012

compiling on freebsd:

g++ -o palindrome -I /usr/local/include -L /usr/local/lib palindrome.cpp -lgmp

@mutaku
Copy link
Author

mutaku commented Dec 5, 2012

Made an output stuffs gist:
https://gist.github.com/4219658

So we don't create scrolling mess here.

@mutaku
Copy link
Author

mutaku commented May 28, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment