demerphq has asked for the wisdom of the Perl Monks concerning the following question:

I need a function (call it 'fix' for lack of a better name) to find the value $N that is an even multiple of $m that is larger than or equal to $n. (See note at bottom for clarification of "even multiple") The best I came up with requires control flow logic and I was wondering if anyone could suggest something better, preferably that doesn't require an 'if'.

sub fix{ my ($n,$m)=@_; my $M = $n % $m; $M ? $n + $m - $M : $n } print "<table width='80%' align='center' border='1'>\n"; print "<tr><th></th><th colspan='4'>\$m</th></tr>\n"; for my $n (0..20) { print " <tr>".join("", map { "<th>$_</th>" } '$n',2..5),"</tr>\n" unless $n; print " <tr>".join("", map { "<td>$_</td>" } $n,map fix($n,$_),2. +.5),"</tr>\n"; } print "</table>"; __END__
(Sample output below:)
 $m
$n2345
00000
12345
22345
34345
44645
56685
666810
789810
889810
91091210
1010121210
1112121215
1212121215
1314151615
1414151615
1516151615
1616181620
1718182020
1818182020
1920212020
2020212020

Note that $m and $n may be any non negative integer,( and thus be odd, or even prime), so you can't use binary operators like '&'.


---
demerphq

    First they ignore you, then they laugh at you, then they fight you, then you win.
    -- Gandhi

    Flux8


• Update:  
To clarify the use of 'evenly' in the above I mean the definition X divides Y evenly if and only if Y % X == 0. Or expressed another way, Y is an even multiple of X if and only if Y % X == 0. Sorry, I'm not much of a mathematician so I forget the textbook definitions and terms for these things.


Replies are listed 'Best First'.
•Re: Best way to make sure a number is an even multiple of another?
by merlyn (Sage) on Oct 21, 2004 at 13:08 UTC

      Yeah, thats it. Thanks and ++ merlyn. I knew it was possible, I just couldn't see past the damn if. :-)


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi

        Flux8


        I may have misunderstood this, but with $n = 19 and $m = 3, fix gives 21 which is not an even multiple of 3.

        Why not just use $m * $n * 2 ?

Re: Best way to make sure a number is an even multiple of another?
by castaway (Parson) on Oct 21, 2004 at 12:58 UTC
    Cheat, no if():
    sub fix{ my ($n,$m)=@_; my $M = $n % $m; $M && return $n + $m - $M; $n; }

    C.

Re: Best way to make sure a number is an even multiple of another?
by itub (Priest) on Oct 21, 2004 at 13:00 UTC
    Perhaps something like:
    use POSIX qw(ceil); sub fix { my ($n, $m) = @_; ceil($n/$m)*$m; }
Re: Best way to make sure a number is an even multiple of another?
by Corion (Patriarch) on Oct 21, 2004 at 13:22 UTC

    For the fun of it, I wanted to give the thing a closed, whole-integer form. The problem was, that a "close fitting" was wanted, and all I had was the following expression:

    my $M; my $n; my $remaining = $n % $M; my $next_multiple = $n + $M - $remaining;

    This means, that my calculation always was one multiple too large whenever $remaining was zero. So I needed to fudge that a bit, by the idea that, if I made $n one smaller in one part, and added that one later on, I could maybe shift the error into the right direction:

    for my $n (1..20) { print "$n:\t"; for my $M (2..5) { my $remaining = ($n-1) % $M; # subtract one from the remainder my $next_multiple = $n-1 + $M - $remaining; print "$next_multiple\t"; }; print "\n"; };

    or, as my unreadable shell test expression:

    perl -e "my $n=20; for(@ARGV){$x=$n-1+($_-($n-1)%$_);print qq($n\[$_] +:$x\n)}" 2 3 4 5 6 7 8 9 10 # resp. $x=$n-1+($M-($n-1)%$M)
Re: Best way to make sure a number is an even multiple of another?
by bart (Canon) on Oct 21, 2004 at 13:20 UTC
    Just for the record:
    sub fix { my($n, $m) = @_; $n + ($n*$m - $n)%$m; }
      What's your rationale for the multiplication? It seems to work fine with "$n*" deleted.

      Caution: Contents may have been coded under pressure.
        I don't trust modulo on a negative number. I'm never sure what value -2 % 5 will have. 3 or -2? I believe it depends on the language.

        So, I want an easy, reliable way to add a multiple ($i) of $m so that $i - $n >= 0. The exact number doesn't matter, as

        ($r*$m + $s) % $m == $s % $m
        for any non-negative integers $r and $s, and positive integer $m.

        $m*$n is both a multiple of $m, and at least as large as $n.

Re: Best way to make sure a number is an even multiple of another?
by Limbic~Region (Chancellor) on Oct 21, 2004 at 13:04 UTC
    demerphq,
    I am not sure how large your numbers are, so the following approach may not be what you need/want:
    • Perform prime factorization on the number
    • Math::Big::Factors or factors if the numbers are smaller than 2^32
    • Look through all combinations (multiplying factors) until you find one that meets your criteria
    If I get a chance, I might even try and implementation.

    Cheers - L~R

      $n will be small. Assume its a 16 bit integer.


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi

        Flux8


        demerphq,
        I am guilty. I am guilty of not spending enough time reading your question to see what you were asking for before jumping to conclusions. The conclusion I jumped to is that you wanted the largest factor greater than or equal to a certain number. To that end, here is code that I believe does that. While I know that this isn't what you asked for, it might be of benefit to someone.

        Cheers - L~R

Re: Best way to make sure a number is an even multiple of another?
by Roy Johnson (Monsignor) on Oct 21, 2004 at 13:50 UTC
    sub fix2 { my $n = shift; # must be greater or equal to this (assert >= 0) my $m = shift; # must be multiple of this (assert > 0) $n + ($m-$n)%$m; }

    Caution: Contents may have been coded under pressure.
Re: Best way to make sure a number is an even multiple of another?
by thospel (Hermit) on Oct 21, 2004 at 16:20 UTC
    Assuming $n is any integer and $m is a positive integer (if $m is negative it finds the first equal or below instead of equal or above)
    -$n%$m+$n
    (don't use this under "use integer")
Re: Best way to make sure a number is an even multiple of another?
by TomDLux (Vicar) on Oct 21, 2004 at 14:55 UTC

    M goes into N int( N/M ) times, with a remainder of M%N.

    So, M * (1 + int( N/M )) will be the smallest multiple of M greater then N.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      The trick is that what the OP wanted was greater than or equal to, without doing an if (or equivalent). Here's one way to modify your suggestion:
      $m*(($n%$m > 0) + int($n/$m));

      Caution: Contents may have been coded under pressure.