in reply to Triangle Numbers Revisited

Just off the top of my head:

In the inner while loop, it looks like you're testing each $i, $j to see if it's triangular (although I must confess, I don't really understand p_tri). Since we can enumerate triangle numbers (if Jobby is to be believed), why not iterate through those?

Edit: Are you sure you're getting correct solutions? It looks like $prev starts out as a triangular number, but then you just decrement it. (Aside: if this algorithm gives you consistently correct solutions, then the greatest triangular number lower than n is always present in n's triangular decomposition. I think.) Man, I'm an idiot. Since when is $prev a trinum?

Edit 2: Oh, I get it, p_tri returns the rank of the previous triangular number, not the number itself... although it returns zero when given a triangular number, which is weird but useful.

Edit 3: Here's the start of an implementation. It's not as fast as the code posted above (by about a factor of two, if Unix time is to be trusted), probably because it calculates a lot of trinums and makes a lot of function calls. I'm posting it more or less as a proof of concept.

Edit 4: Inlining the calls to &trinum results in slightly faster code than Limbic~Region's. Code updated, benchmarks added.

#! /usr/bin/perl -w use strict; # trinum(n) returns the nth triangular number sub trinum { my ($n) = @_; return $n * ($n+1) * 0.5; } # prev_trinum(n) returns the RANK OF the greatest triangular number le +ss # than n. # Code blatantly ripped off from Limbic~Region [id://399054] sub prev_trinum { my $num = shift; my $x = ( sqrt( 8 * $num + 1 ) + 1 )/ 2; my $t = int $x; return $t == $x ? 0 : --$t; } # trinum_decomp(n) tries to find a three-triangular-number decompositi +on # of n. Based on L~R's method from the post cited above, but # enumerates trinums rather than guessing. sub trinum_decomp { my ($n) = @_; my $prev = &prev_trinum($n); return ($n, 0, 0) unless $prev; while($prev) { my $triprev = ($prev * $prev + $prev)/2; my $diff = $n - $triprev; my @tail = &twonum_decomp($diff); if(defined $tail[0]) { return ($triprev, @tail); } $prev--; } warn "Can't find trnum decomp for $n\n"; return (-1, -1, -1); # ugly } # twonum_decomp(n) tries to find a two-triangular-number decomposition # of n. If such a decomposition does not exist, returns undef. sub twonum_decomp { my ($n) = @_; my $prev = &prev_trinum($n); return ($n, 0) unless $prev; while($prev) { my $triprev = ($prev * $prev + $prev)/2; my $i = 1; my $tri_i = ($i * $i + $i)/2; do { if($tri_i + $triprev == $n) { return ($tri_i, $triprev); } $i++; $tri_i = ($i * $i + $i)/2; } while($triprev + $tri_i <= $n); $prev--; } return undef; } my $target = $ARGV[0] || 314159; print join(',', &trinum_decomp($target)); __END__ mjolson@riga:~/devel/scratch Wed Oct 13-18:38:42 583 >time ./trinum 987654321 987567903,14028,72390 real 0m0.089s user 0m0.060s sys 0m0.000s mjolson@riga:~/devel/scratch Wed Oct 13-18:18:25 578 >time ./limbic_trinum 987654321 987567903, 14028, 72390 real 0m0.106s user 0m0.090s sys 0m0.000s

--
Yours in pedantry,
F o x t r o t U n i f o r m

Replies are listed 'Best First'.
Re^2: Triangle Numbers Revisited
by Limbic~Region (Chancellor) on Oct 14, 2004 at 00:09 UTC
    All,
    In HTML comments, I have provided a spoiler explanation of how the code works.

    Update: 2013-10-24 When I first wrote this node, we didn't have spoiler tags. The HTML comments remain but I have also added spoiler for those who don't want to view the source.

    0. Since a triangular number is ( n^2 + n ) / 2 and that can be expressed in the quadratic formula, it is easy to determine if a number is triangular or not 1. The code determines if the target is a triangular number. As a side effect, if it is not a triangular number it returns the largest triangular number smaller than the target (p is short for previous) 2. It then determines the difference between the target and the previous triangular number and does a divide and conquer approach - for instance, if the difference was 6 0 6 1 5 2 4 3 3 3. If it finds 2 new targets that are both triangular, it returns. If it doesn't it just drops down to the previous number and starts the process again Incidently, I do not think (haven't tested) this approach will benefit that much from caching known triangular numbers. It was pretty fast no matter what numbers I threw at it and it would chew up memory pretty quickly.

    Cheers - L~R

Re^2: Triangle Numbers Revisited
by Limbic~Region (Chancellor) on Oct 14, 2004 at 02:34 UTC
    FoxtrotUniform,
    The trouble with selecting a single number to benchmark from is that it may favor one method over the other. With this in mind, I created another rudimentary benchmark that shows the two methods are fairly equal: I haven't spent any time thinking about optimizations, but if I come up with any tomorrow I will post it along with a "real" benchmark.

    Cheers - L~R

      The trouble with selecting a single number to benchmark from is that it may favor one method over the other.

      Good catch. Another problem I ran into was that the load on my test system was varying (lab machine, someone was logged in remotely doing a bunch of matlab foolery), so I ended up getting quite different "real" time results with the same input... which is probably what a casual reader would check.

      It would be interesting to know what kinds of inputs favour whose method, and (ideally) why.

      --
      Yours in pedantry,
      F o x t r o t U n i f o r m

Re^2: Triangle Numbers Revisited
by Limbic~Region (Chancellor) on Oct 14, 2004 at 04:54 UTC
    FoxtrotUniform,
    I couldn't sleep as I had several optimizations pop into my head I just had to try. I played around with caching known triangular numbers as well as results of known non-triangular numbers. I only got a marginal speed increase which I guessed would be the case earlier in the CB. Moving away from caching, I tried a combination of my method and your method with dramatic results: From 1 minute to just over 3 seconds.

    Cheers - L~R