in reply to Re: Elegantly map fizz to buzz
in thread Elegantly map fizz to buzz
Hi, I see you've attempted to generalize the problem by passing the moduli along as arguments. However, this complicates the matters considerably. Numbers multiple of some value form a set; all possible intersections of those form a powerset. E.g. for (3, 5, 7) you have the 305071, 305170, ..., 315171 subsets to consider.
Here's my attempt at a generic version. I'm not quite sure if it's sound in principle. (Now where are the resident mathematicians?)
Again, the naive version and a smarter one. Subseries are summed with alternating signs according to cardinality: that way each number is tallied exactly once in the end.#! /usr/bin/perl -wl use feature qw( signatures ); no warnings qw( experimental::signatures ); use List::Util qw( sum0 product reduce any ); sub gcd($a, $b) { !$b ? $a : gcd($b, $a % $b) } sub lcm { reduce { $a * $b / gcd($a, $b) } @_ } sub fk1($max, @A) { sum0 grep { my $x = $_; any {$x % $_ == 0} @A } 1 .. $max } sub fk2($max, @A) { sum0 map { (parity($_) || -1) * sum_every_nth($max, lcm_select($_, @A)) } 1 .. (1<<@A)-1 } sub sum_every_nth($max, $n) { int($max/$n) * int($max/$n + 1) / 2 * $n } sub lcm_select($sel, @A) { lcm @A[grep {$sel>>$_ & 1} 0..$#A] } sub parity { unpack "%1b*", pack "J", shift } my @A = (3, 5, 74017, 74027, 74047, 74077); print join ' ', 0+$_, fk1($_, @A), fk2($_, @A) while <>;
Where the guarantee is given that moduli are mutually prime, one can use product() instead of lcm(). Then, if the limit $max is much greater than lcm(@A), one may reduce it first: e.g. with lcm(3, 5) you have a wheel15 going on. Etc. Talk about trivial exercises...
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^3: Elegantly map fizz to buzz
by BrowserUk (Patriarch) on May 17, 2016 at 21:37 UTC | |
by oiskuu (Hermit) on May 18, 2016 at 18:06 UTC | |
by BrowserUk (Patriarch) on May 18, 2016 at 18:25 UTC |