Polite Numbers
December 17, 2010
The list of numbers corresponding to the odd divisor d of the number n is calculated by the polite
function; the first arm of the if
is the “normal” case, and the second arm of the if
is taken when the list includes 0 or negative numbers:
(define (polite n d)
(let ((d2 (quotient d 2)) (n/d (/ n d)))
(if (< d2 n/d)
(range (- n/d d2) (+ n/d d2 1))
(range (+ (abs (- n/d d2)) 1) (+ n/d d2 1)))))
We make a list of all the sets of consecutive integers by iterating through the odd divisors of the input number:
(define (polites n)
(if (= (expt 2 (ilog 2 n)) n) '()
(let loop ((ds (cdr (filter odd? (divisors n)))) (ps '()))
(if (null? ds) (reverse ps)
(loop (cdr ds) (cons (polite n (car ds)) ps))))))
It’s easy to compute the politeness of a number:
(define (politeness n)
(if (= (expt 2 (ilog 2 n)) n) 0
(length (cdr (filter odd? (divisors n))))))
Here are some examples:
> (politeness 15)
3
> (polites 15)
((4 5 6) (1 2 3 4 5) (7 8))
> (politeness 28)
1
> (polites 28)
((1 2 3 4 5 6 7))
> (politeness 33)
3
> (polites 33)
((10 11 12) (3 4 5 6 7 8) (16 17))
We used range, filter, sort, unique, and ilog from the Standard Prelude, and factors
and divisors
from a previous exercise. You can run the program at http://programmingpraxis.codepad.org/YgFEcg8Q.
[…] Praxis – Polite Numbers By Remco Niemeijer In today’s Programming Praxis exercise, our task is to list all the ways that a number can be written as the […]
My Haskell solution (see http://bonsaicode.wordpress.com/2010/12/17/programming-praxis-polite-numbers/ for a version with comments):
here my commented solution: http://www.gleocadie.net/?p=519&lang=en
Hi,
Here’s my solution in Scala. I’m sure there’s a better way to calculate divisors and also a better way to expand the length of array rather than just incrementing it.
import scala.collection.mutable._
object Divisors
{
def generate(n:Int) =
{
var divisors = ListBuffer[Int]()
for(i <- 1 to n)
{
if(n % i == 0)
divisors += i
}
divisors.toList
}
}
object PoliteNumbers
{
def calculate(number:Int) =
{
var politeness = 0
for(divisor 1 && divisor < number && divisor % 2 == 1)
{
politeness += 1
var start = divisor
var end = divisor
var sum = 0
while(sum < number)
{
start -= 1
end += 1
sum = (start to end).sum
}
println(start+" to "+end+" : "+(start to end).sum)
}
}
println("Politeness is "+politeness)
}
}
val number = Integer.parseInt(args(0))
PoliteNumbers.calculate(number)
Brain’s a little worn out from moving/end of semester, so I Pythoned the Scheme procedures.
I don’t think that we need to check whether n is a power of 2, because it will have no odd
divisors if it is.
Here is a Perl solution. I hope I uploaded the code correctly
#!/usr/bin/perl
print "Enter an integer: ";
$number = ;
chomp($number);
$politeness = 0;
for($div = 3; $div <= $number; $div += 2) { #iterate through odd potential divisors
if($number % $div == 0) { #found an odd divisor
$codiv = $number / $div; #set the codivisor
printline($codiv-($div-1)/2,$div);
$politeness++;
}
}
print "Politeness of $number is $politeness\n";
sub printline
{
my $base = $_[0];
my $count = $_[1];
if($base < 0) { #cancel off the negative part
$base = -$base + 1;
$count = $count - (2*$base) + 1;
}
print "$base";
$count--;
until($count == 0){
$base++;
print " + $base";
$count--;
}
print " = $number\n";
}
Ok, maybe the code will look better like this
a “functional” python version
factors(n) returns a sequence of (p,k) tuples, where p^k is in the factorization of “n”
from itertools import dropwhile, imap, islice, product, repeat, starmap
from operator import mul
def geometric_series(p, k):
”’return generator for geometric series p^0, p^1, p^2, … p^k.”’
return (pow(p, n) for n in range(k+1))
def odd_divisors(n):
”’generates all odd divisors of “n”.”’
odd_factors = dropwhile(lambda t:t[0]&1==0, factors(n))
sets_of_odd_factors = product(*starmap(geometric_series, odd_factors))
return imap(reduce, repeat(mul), sets_of_odd_factors, repeat(1))
def polites(n):
”’generates lists of consecutive integers that sum to “n”.”’
for divisor in islice(odd_divisors(n), 1, None):
hi = n/divisor + divisor/2
low = max(hi – divisor + 1, divisor – hi)
yield range(low,hi+1)
def politeness(n):
”’return number of sequences that sum to “n”.”’
return sum(imap(bool, odd_divisors(n))) – 1
#test:
for n in (28, 33, 42, 64):
print ‘the politeness of %d is %d:’ % (n, politeness(n))
for lst in polites(n):
print ‘ %s = %d’ % (‘ + ‘.join(str(i) for i in lst), sum(lst))
print
[/soucecode]
New and improved! Now with formating!
A ruby version (kind of ugly though) …
in F#
Here’s a non-optimal method written in JavaScript. Computational time is O(n/2).
The function will return a 2d array; first value is the starting number, second value is the number of steps that need to be taken. For example if checkNum() returns [[1, 4]] this means to start at 1 and add a total of 4 numbers together in sequence (e.g. 1+2+3+4).
JS Solve:
function checkNum(N) {
//We will return arrays of [start, ct]
var results = [],
//Longest possible sequence count is 1+2+3+4+…
//For a given sequence length ct, this is N=(ct^2+ct)/2
//Solving for ct, we find that the longest possible sequence is…
maxCt = Math.ceil((Math.sqrt(8*N+1)-1)/2),
//If N is even, ct cannot be 2.
ct = ((N&1)==0)?3:2,
center, frac;
for (;ct<=maxCt;ct++) {
center=N/ct, frac=center-Math.floor(center);
if (
//we've divided evenly into a sequence of ct (odd
(frac==0 && (ct&1)==1) ||
// or even)
(frac==0.5 && (ct&1)==0)
) {
results.push([center-(ct-1)/2, ct]);
}
}
return results;
}
Trying again, after reading the help…
function checkNum(N) {
//We will return arrays of [start, ct]
var results = [],
//Longest possible sequence count is 1+2+3+4+...;
//For a given sequence length ct, this is N=(ct^2+ct)/2
//Solving for ct, we find that the longest possible sequence is...
maxCt = Math.ceil((Math.sqrt(8*N+1)-1)/2),
//If N is even, ct cannot be 2.
ct = ((N&1)==0)?3:2,
center, frac;
for (;ct<=maxCt;ct++) {
center=N/ct, frac=center-Math.floor(center);
if (
//we've divided evenly into a sequence of ct (odd
(frac==0 && (ct&1)==1) ||
// or even)
(frac==0.5 && (ct&1)==0)
) {
results.push([center-(ct-1)/2, ct]);
}
}
return results;
}
One more time. Really should have an edit option.