http://www.perlmonks.org?node_id=482012


in reply to Algorithm for cancelling common factors between two lists of multiplicands

The same exampled worked through:

  • @a = ( 10, 20, 33, 45, 60 );
  • @b = ( 2, 5, 10, 12, 16, 23, 45 );

    Eliminate common elements:

  • @a = ( 20, 33, 60 );
  • @b = ( 2, 5, 12, 16, 23 );

    Cancel 2 with 20 to give 1 and 10 (discard the 1):

  • @a = ( <strike>20</strike><ins>10</ins>, 33, 60 );
  • @b = ( <strike>2</strike><ins>1</ins>, 5, 12, 16, 23 );

    Cancel 5 with 10 to give 1 and 2:

  • @a = ( <strike>10</strike><ins>2</ins>, 33, 60 );
  • @b = ( <strike>5</strike><ins>1</ins>, 12, 16, 23 );

    Cancel 2 with 12 to give 1 and 6 (discard the 1 )

  • @a = ( 2, 33, 60 );
  • @b = ( 12, 16, 23 );

    Cancel 6 with 60 to give 1 and 10 (discard the 1 )

  • @a = ( 33, 60 );
  • @b = ( 6, 16, 23 );

    Cancel 16 with 10 to give 8 and 5

  • @a = ( 33, 10 );
  • @b = ( 16, 23 );

    Result: (Other reductions are possible!)

  • @a = ( 33, 5 );
  • @b = ( 8, 23 );

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
  • Replies are listed 'Best First'.
    Re^2: Algorithm for cancelling common factors between two lists of multiplicands
    by ikegami (Pope) on Aug 08, 2005 at 20:42 UTC
      If the solution in my earlier node doesn't return what you want, this surely will:
      use Math::BigInt (); *gcd = \&Math::BigInt::bgcd; sub embiggen { local $_ = @_ ? $_[0] : $_; return Math::BigInt->new($_); } my @a = ( 10, 20, 33, 45, 60 ); my @b = ( 2, 5, 10, 12, 16, 23, 45 ); # my %a = map { $_ => 1 } @a; # my %b = map { $_ => 1 } @b; # # my @c = map embiggen, # grep { !$b{$_} } # @a; # # my @d = map embiggen, # map { Math::BigInt->new($_) } # grep { !$a{$_} } # @b; my @c = @a; my @d = @b; foreach my $c (@c) { foreach my $d (@d) { my $gcd = gcd($c, $d); $c /= $gcd; $d /= $gcd; } } @c = grep { $_ != 1 } @c; @d = grep { $_ != 1 } @d;
      or this messier but potentially faster alternative:
      use Math::BigInt (); use Math::Big::Factors (); *gcd = \&Math::BigInt::bgcd; *factor = \&Math::Big::Factors::factors_wheel; sub embiggen { local $_ = @_ ? $_[0] : $_; return Math::BigInt->new($_); } my @a = ( 10, 20, 33, 45, 60 ); my @b = ( 2, 5, 10, 12, 16, 23, 45 ); # my %a = map { $_ => 1 } @a; # my %b = map { $_ => 1 } @b; # # my @c = map embiggen, # grep { !$b{$_} } # @a; # # my @d = map embiggen, # grep { !$a{$_} } # @b; my @c = @a; my @d = @b; my %c_factors; foreach my $c_idx (0..$#c) { my @c_factors = factor($c[$c_idx]); foreach my $c_factor (@c_factors) { push(@{$c_factors{$c_factor}}, $c_idx); } } foreach my $d (@d) { my @d_factors = factor($d); foreach my $d_factor (@d_factors) { next unless $c_factors{$d}; next unless @{$c_factors{$d}}; my $c_idx = shift(@{$c_factors{$d}}); $c[$c_idx] /= $d_factor; $d /= $d_factor; } } @c = grep { $_ != 1 } @c; @d = grep { $_ != 1 } @d;
      Both untested.
        Update I wrote this when we were trying to figure out the exact output. Not relevant anymore

        ikegami,

        Your first should work correctly. Did not read the second one yet. One thing you want to note when you test this code is that the result will not match exactly what BrowserUk had coz the cancellation process is kind of subjective when done by humans as opposed to computers

        take for example if i have 15 * 3 * 12 / 9 * 17

        then I could either do 5 * 12/17 or 15 * 4/17

        I guess the final solution should match product of the elements in the array

        Another thing, you might get a tiny little benefit if you check for the elements == 1 inside the loop construct and pop them out. I am not sure if that is even possible.

        BTW, i think you can add next if $c == 1 or $d == 1; to avoid computing gcd's on 1's.

        -SK

    Re^2: Algorithm for cancelling common factors between two lists of multiplicands
    by sk (Curate) on Aug 08, 2005 at 20:39 UTC
      Here is my shot at a pseudo-code

      1. Pick the first element of array a

      2. Calcuate the GCD(a[0],b[0]) or better yet a[0] = mod (a[0], gcd(a[0],b[0]) and b[0] =mod (a[0], gcd(b[0],b[0]) (just divide elements by gcd, it should be an integer)

      3. If anything is 1 pop it out of the list

      4. Go through the list of elements for b with a[0] again. Do this until all elements are scanned in b.

      5. Take the second element in a and repeat the process.

      Does this work?

      I shall post the code if i get a chance to implement it

      -SK

      PS: This reminds of division in high school days ;)

        PS: This reminds of division in high school days ;)

        I guess that's where the "cancelling out" phrase comes from :)

        Maybe the confusion comes from the fact that most people younger than me never learned to cancel out their equations because they all used calculators from an early age.

        They probably don't know what "casting out the nines" is either.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
    Re^2: Algorithm for cancelling common factors between two lists of multiplicands
    by gri6507 (Deacon) on Aug 08, 2005 at 20:39 UTC
      Why not do this:

      foreach, element in an array, factor to primes (i.e. if starting from 10, then 1*2*5). Create a hash for for the first array where keys are factors of each element and values are the number of times each factor has occurred. Repeat for second array. "Subtract" the two hashes. Multiply the resulting keys by their respecting values --> should give you the elements of the resulting array.