Permalink
Browse files

New arithmetic solutions

  • Loading branch information...
philandstuff committed Sep 6, 2010
1 parent 88492e4 commit 68db7de3e26399c116d7b59b31c864a05a282d8d
Showing with 132 additions and 3 deletions.
  1. +4 −3 99-problems/P34-rhebus.pl
  2. +35 −0 99-problems/P35-rhebus.pl
  3. +38 −0 99-problems/P36-rhebus.pl
  4. +55 −0 99-problems/P37-rhebus.pl
@@ -26,8 +26,8 @@ (Int $a, Int $b)
# Example 1: iteration
-sub totient_phi_i (Int $n) {
- return 1 if $n ~~ 1;
+multi totient_phi_i (1 --> Int) { 1 }
+multi totient_phi_i (Int $n --> Int) {
my $total = 0;
for 1..^$n -> $k { $total++ if $n coprime $k }
return $total;
@@ -38,7 +38,8 @@ (Int $n)
# Example 2: «coprime« hyper operator
-sub totient_phi (Int $n) {
+multi totient_phi (1 --> Int) { 1 }
+multi totient_phi (Int $n --> Int) {
return 1 if $n ~~ 1;
return [+] ($n «coprime« list(1..^$n));
}
View
@@ -0,0 +1,35 @@
+use v6;
+
+# Specification:
+# P35 (**) Determine the prime factors of a given positive integer.
+# Construct a flat list containing the prime factors in ascending order.
+# Example:
+# > say ~prime_factors 315
+# 3 3 5 7
+
+
+sub prime_factors (Int $n) {
+ my $residue = $n;
+ gather for (2,3,*+2 ... $n) -> $k {
+ while $residue %% $k {
+ # try 'take 0+$k' to work around a known rakudo issue (2010-09-05)
+ take $k;
+ $residue /= $k;
+ }
+ last if $residue == 1;
+ # This if block is an optimisation which reduces number of iterations
+ # for numbers with large prime factors (such as large primes)
+ # It can be removed without affecting correctness.
+ if $k > sqrt $residue {
+ take $residue;
+ last;
+ }
+ }
+}
+
+say ~prime_factors $_ for 2..20;
+say ~prime_factors(315);
+say ~prime_factors(1723);
+
+
+# vim:ft=perl6
View
@@ -0,0 +1,38 @@
+use v6;
+
+# Specification:
+# P36 (**) Determine the prime factors of a given positive integer (2).
+# Construct a list containing the prime factors and their multiplicity.
+# Example:
+# > prime_factors_mult(315).perl.say
+# ([3,2],[5,1],[7,1])
+#
+# Hint: The problem is similar to problem P13.
+
+
+sub prime_factors_mult (Int $n) {
+ my $residue = $n;
+ gather for (2,3,*+2 ... $n) -> $k {
+ my $mult=0;
+ while $residue %% $k {
+ $mult++;
+ $residue div= $k;
+ }
+ take [$k, $mult] if $mult;
+ last if $residue == 1;
+ # This if block is an optimisation which reduces number of iterations
+ # for numbers with large prime factors (such as large primes)
+ # It can be removed without affecting correctness.
+ if $k > sqrt $residue {
+ take [$residue,1];
+ last;
+ }
+ }
+}
+
+say prime_factors_mult($_).perl for 1..20;
+prime_factors_mult(315).perl.say;
+prime_factors_mult(1723).perl.say;
+
+
+# vim:ft=perl6
View
@@ -0,0 +1,55 @@
+use v6;
+
+# Specification:
+# P37 (**) Calculate Euler's totient function phi(m) (improved).
+# See problem P34 for the definition of Euler's totient function. If the
+# list of the prime factors of a number m is known in the form of
+# problem P36 then the function phi(m) can be efficiently calculated as
+# follows: Let ((p1 m1) (p2 m2) (p3 m3) ...) be the list of prime
+# factors (and their multiplicities) of a given number m. Then phi(m)
+# can be calculated with the following formula:
+#
+# phi(m) = (p1-1) * p1 ** (m1-1) * (p2-1) * p2 ** (m2-1)
+# * (p3-1) * p3 ** (m3-1) * ...
+
+
+# Straight from P36-rhebus.pl
+sub prime_factors_mult (Int $n) {
+ my $residue = $n;
+ gather for (2,3,*+2 ... $n) -> $k {
+ my $mult=0;
+ while $residue %% $k {
+ $mult++;
+ $residue div= $k;
+ }
+ take [$k, $mult] if $mult;
+ last if $residue == 1;
+ if $k > sqrt $residue {
+ take [$residue,1];
+ last;
+ }
+ }
+}
+
+
+# 1. One-liner version
+say "phi($_): ", [*] prime_factors_mult($_).map({ ($_[0]-1) * $_[0] ** ($_[1]-1) })
+ for 1..20;
+say [*] prime_factors_mult(315).map: { ($_[0]-1) * $_[0] ** ($_[1]-1) };
+
+
+# 2. sub version
+# note that when prime_factors_mult returns an empty list, [*] returns the
+# multiplicative identity 1. This means we don't need to special-case
+# totient(1) like in P34-rhebus.pl
+sub totient (Int $n) {
+ my @factors = prime_factors_mult($n);
+ return [*] @factors.map: {
+ ($_[0]-1) * $_[0] ** ($_[1]-1)
+ }
+}
+
+say "phi2($_): ", totient($_) for 1..20;
+say "phi2(315): ", totient(315);
+
+# vim:ft=perl6

0 comments on commit 68db7de

Please sign in to comment.