Three triangular numbers

19

0

Description

There have been quite a few other challenges concerning these numbers before, and I hope this one is not among them.

The n th triangular number equals the sum of all natural numbers up to n, simple stuff. There are a wikipedia page and an entry at OEIS, for those who wish to inform themselves further.

Now, Gauss found out that every natural number may be expressed as a sum of three triangular numbers (these include 0), and it is fine to have one number more than once, e.g. 0 + 1 + 1 = 2.

Challenge

Your task is to write a program or function, given a natural number (including 0), prints three triangular numbers that sum up to the argument. You may print the numbers separeted by spaces, as an array, or by another method you like. However, it is forbidden to use any builtin functions to directly get an array, a range or any other form of collection containing a list of triangular numbers (for instance a single atom that yields the range).

Test cases

9 -> 6 + 3 + 0 or 3 + 3 + 3
12 -> 6 + 6 + 0 or 6 + 3 + 3 or 10 + 1 + 1
13 -> 6 + 6 + 1
1 -> 1 + 0 + 0
0 -> 0 + 0 + 0

Note: If there is more than one possible combination, you may print any or all, but you must print any combination only once, eliminating all combinations that are a result of rearranging other combinations. I'd really appreciate a try-it link and an explanation, I really love to see how you solve the problem ;)

This is , so standard loopholes apply. May the shortest answer in bytes win!

racer290

Posted 2017-07-17T12:44:18.640

Reputation: 1 043

Question was closed 2017-07-18T09:02:05.337

You're welcome to! ;) – racer290 – 2017-07-17T12:47:54.997

1For 12 you can also do 1 + 1 + 10. – Erik the Outgolfer – 2017-07-17T12:59:04.277

I guess INPUT a: PRINT 0,0,a would be a loophole? – steenbergh – 2017-07-17T13:25:32.280

1@steenbergh a won't always be a triangular number – Felipe Nardi Batista – 2017-07-17T13:28:36.430

3I can parse "builtin functions to directly get an array, a range or any other form of collection containing a list of triangular numbers" in two ways, but neither of them makes sense. The first prohibits all builtins which directly get an array, but that seems to prohibit all use of arrays in every language I know; the other prohibits builtins to "directly get ... a range ... containing a list of triangular numbers", but I don't know what that would mean. – Peter Taylor – 2017-07-17T13:38:46.550

@PeterTaylor that means that no builtin functions returning a collection of triangular numbers and taking no arguments are banned - you must generate the required collection by using a formula or the sum of the necessary natural numbers instead. – racer290 – 2017-07-17T13:42:11.090

2So built-in functions which take an argument n and return a list of the first n triangle numbers are permitted? That feels rather targetted against some specific language, although I don't know which. – Peter Taylor – 2017-07-17T13:49:32.173

4I urge you to lift this restriction. I promise you it won't improve the inter-language answer quality or fairness in the way you think. – Lynn – 2017-07-17T13:51:16.010

1For 13 you can do 10+3+0, right? – Kevin Cruijssen – 2017-07-17T14:17:13.657

@PeterTaylor I'm not, I just want to avoid that one can just use a collection of triangular numbers that is directly provided by the language they use. I'm not familiar with any golfing language, so I can't know if there is a builtin in some language. I want them to solve the generation of the numbers on their own, because there are a different ways, and it gives the challenge a bit of a twist, in my opinion. – racer290 – 2017-07-17T15:07:35.747

@KevinCruijssen Yep. – racer290 – 2017-07-17T15:08:11.313

1@racer290 He's asking because for the other test cases you listed all possible answers. – mbomb007 – 2017-07-17T15:43:02.917

Answers

8

05AB1E, 10 bytes

Code:

ÝηO3ãʒOQ}¬

Explanation:

Ý             # Compute the range [0 .. input]
 η            # Get the prefixes
  O           # Sum each prefix to get the triangle numbers
   3ã         # Cartesian repeat 3 times
     ʒ  }     # Keep elements that
      OQ      #   have the same sum as the input
         ¬    # Retrieve the first element

Uses the 05AB1E encoding. Try it online!

Adnan

Posted 2017-07-17T12:44:18.640

Reputation: 41 965

Ahhh... Yep; that'd do it. – Magic Octopus Urn – 2017-07-17T14:18:27.183

7

Python 2, 99 bytes

from random import*
n=input()
while 1:b=sample([a*-~a/2for a in range(n+1)]*3,3);n-sum(b)or exit(b)

Try it online!

I'm pretty amazed this is shorter than itertools or a triple list comprehension! It (eventually) spits out a random answer every time you run it.

Two 102s:

n=input();r=[a*-~a/2for a in range(n+1)];print[(a,b,c)for a in r for b in r for c in r if a+b+c==n][0]
def f(n):r=[a*-~a/2for a in range(n+1)];return[(a,b,c)for a in r for b in r for c in r if a+b+c==n][0]

itertools looks to be 106:

from itertools import*;lambda n:[x for x in product([a*-~a/2for a in range(n+1)],repeat=3)if sum(x)==n][0]

Lynn

Posted 2017-07-17T12:44:18.640

Reputation: 55 648

+1 for random output. :) I'm also surprised that gives the shortest solution (thus far). – Kevin Cruijssen – 2017-07-17T14:41:08.397

Thank you very much for the method. The corresponding Ruby code has 57 bytes.

– Eric Duminil – 2017-07-17T17:20:06.747

3

Jelly, 12 bytes

0r+\œċ3S=¥Ðf

Try it online!

How it works

0r+\œċ3S=¥Ðf   input: n
0r             [0 1 ... n]
  +\           cumsum
    œċ3        combinations of 3 elements, with repetition
          Ðf   filter on
       S          sum
        =         equals n

Leaky Nun

Posted 2017-07-17T12:44:18.640

Reputation: 45 011

Please consider adding an explanation.. =) – racer290 – 2017-07-17T12:58:16.893

2@racer290 done. – Leaky Nun – 2017-07-17T13:00:29.470

3

Brachylog, 13 bytes

⟦⟦ᵐ+ᵐj₃⊇Ṫ.+?∧

Try it online!

How it works

⟦⟦ᵐ+ᵐj₃⊇Ṫ.+?∧  input: n
⟦              [0 1 ... n]
 ⟦ᵐ            [[0] [0 1] [0 1 2] ... [0 1 ... n]]
   +ᵐ          [0 1 3 ... n(n+1)/2]
     j₃        [0 1 3 ... n(n+1)/2 0 1 3 ... n(n+1)/2 0 1 3 ... n(n+1)/2]
       ⊇       is a superset of
        Ṫ      a list of three elements 
         .     which is the output
          +?   which sums up to be the input

Leaky Nun

Posted 2017-07-17T12:44:18.640

Reputation: 45 011

2

MATL, 18 bytes

Q:qYs3Z^t!sG=fX<Y)

This outputs the first result in lexicographical order.

Try it at MATL Online!

Explanation

Q     % Implicitly input n. Add 1
:     % Range (inclusive, 1-based): gives [1 2 ... n+1]
q     % Subtract 1 (element-wise): gives [0 1 ... n]
Ys    % Cumulative sum
3Z^   % Cartesian power with exponent 3. Gives a matrix where each row is a
      % Cartesian tuple
t     % Duplicate
!s    % Sum of each row
G=    % Does each entry equal the input?
f     % Find indices that satisfy that condition
X<    % Minimum
Y)    % Use as row index into the Cartesian power matrix. Implicitly display

Luis Mendo

Posted 2017-07-17T12:44:18.640

Reputation: 87 464

2

Retina, 63 59 bytes

.+
$*
^((^1|1\2)*)((1(?(4)\4))*)((1(?(6)\6))*)$
$.1 $.3 $.5

Try it online! Link includes test cases. (1(?(1)\1))* is a generalised triangular number matcher, but for the first triangular number we can save a few bytes by using ^ for the initial match.

Neil

Posted 2017-07-17T12:44:18.640

Reputation: 95 035

2

Haskell, 66 59 bytes

Thanks for allowing to output all solutions, that was fascinating distraction! I was so happy to not need to extract one solution and be able to just give them all that I didn't notice the cost that comes from avoiding permuted solutions. @Lynn's remark explained that to me and let me save 7 bytes.

f n|l<-scanl(+)0[1..n]=[(a,b,c)|c<-l,b<-l,a<-l,a+b+c==n]!!0

This binds more than enough triangular numbers to l and checks all combinations.

Christian Sievers

Posted 2017-07-17T12:44:18.640

Reputation: 6 366

Isn’t dropping the a>=b,b>=c conditions and just suffixing !!0 to your code also a valid answer? Outputting all solutions doesn’t really help you here. – Lynn – 2017-07-17T18:48:10.390

@Lynn You're right of course, I got distracted. Thanks! – Christian Sievers – 2017-07-17T22:59:42.270

1

Python 3, 119 bytes

lambda n:[l for l in combinations_with_replacement([(t**2+t)/2for t in range(n)],3)if sum(l)==n]
from itertools import*

Try it online!

Thanks to @WheatWizard for saving 12 bytes!

Chase Vogeli

Posted 2017-07-17T12:44:18.640

Reputation: 141

Your map (and perhaps your filter) can be written shorter as a list comprehension. – Post Rock Garf Hunter – 2017-07-17T14:44:42.320

Here's a TIO of the changes implemented. – Post Rock Garf Hunter – 2017-07-17T14:48:51.183

@WheatWizard thanks for the idea, I can't believe I didn't think of a list comprehension for the map – Chase Vogeli – 2017-07-17T14:50:20.570

Filter objects are perfectly valid output, but if you want to output a list you can use a splat like so [*filter(...)] – Post Rock Garf Hunter – 2017-07-17T14:51:47.570

1What I had tried was (x,y,z) for x,y,z in... which is longer than your l for l in... which likely accounts for that difference. – Chase Vogeli – 2017-07-17T14:52:59.307

1

PHP, 351 bytes

$r=[];function f($a=[],$c=0){global$argn,$t,$r;if($c<3){$n=$argn-array_sum($a);$z=array_filter($t,$f=function($v)use($n,$c){return$v>=$n/(3-$c)&&$v<=$n;});foreach($z as$v){$u=array_merge($a,[$v]);if(($w=$n-$v)<1){if(!$w){$u=array_pad($u,3,0);sort($u);if(!in_array($u,$r)){$r[]=$u;}}}else f($u,$c+1);}}}for($t=[0];$argn>$t[]=$e+=++$i;);f();print_r($r);

Try it online!

Jörg Hülsermann

Posted 2017-07-17T12:44:18.640

Reputation: 13 026

1

Mathematica, 63 bytes

(t=#;#&@@Select[Table[i(i+1)/2,{i,0,t}]~Tuples~{3},Tr@#==t&]‌​)&

J42161217

Posted 2017-07-17T12:44:18.640

Reputation: 15 931

With infix syntax and that whack way of getting First that saves a whopping 2 bytes, (t=#;#&@@Select[Table[i(i+1)/2,{i,0,t}]~Tuples~{3},Tr@#==t&])& for 62 bytes. – numbermaniac – 2017-07-18T08:48:44.117

Great, I'll edit – J42161217 – 2017-07-18T08:54:37.860

1

C/C++ - 197 bytes

#include<stdio.h>
#define f(i,l,u) for(int i=l;i<=u;i++)
int t(int n){return n>1?n+t(n-1):n;}
int c(int n){f(a,0,n)f(b,a,n)f(c,b,n)if(t(a)+t(b)+t(c)==n)return printf("%d %d %d\n",t(a),t(b),t(c));}

Blow by blow:

#include<stdio.h>

Needed for printf. Could be elided for certain versions of C

#define f(i,l,u) for(int i=l;i<=u;i++)

Space saving for loop.

int t(int n){return n>1?n+t(n-1):n;}

Recursive triangle evaluator.

int c(int n){f(a,0,n)f(b,a,n)f(c,b,n)if(t(a)+t(b)+t(c)==n)return printf("%d %d %d\n",t(a),t(b),t(c));}

This guy does the heavy lifting. Three nested for loops iterate a, b, c from 0 to n, note that b and c each iterate from the previous value up to n. It's not strictly necessary to trim iteration like that since the return coming in a minute solves the "duplicate" problem.

At the inner level, if the sum of the three triangle numbers == the desired value, print the triangles and return.

You can legally remove the return keyword and convert the return type of c to void to save a few more bytes and print all possible solutions. It is for this reason that iterations are limited, if all loops ran from 0 to n it would cause duplicates.

dgnuff

Posted 2017-07-17T12:44:18.640

Reputation: 111

0

CJam, 26 bytes

{_),[{\_@+}*]3m*_{:+}%@#=}

Port of my MATL answer. This is an anonymous block that expects the input on the stack and replaces it by the output array.

Try it online!

Luis Mendo

Posted 2017-07-17T12:44:18.640

Reputation: 87 464

0

R, 66 bytes

n=scan();b=expand.grid(rep(list(cumsum(0:n)),3));b[rowSums(b)==n,]

Brute force algorithm; reads n from stdin and returns a dataframe where each row is a combination of 3 triangular numbers that add up to n. If necessary, I can return only the first row for +4 bytes.

Try it online!

Giuseppe

Posted 2017-07-17T12:44:18.640

Reputation: 21 077

0

Java 8, 164 bytes

n->{int t[]=new int[n+1],i=0,j=0;for(;i<=n;)if(Math.sqrt(8*i+++1)%1==0)t[j++]=i-1;for(int a:t)for(int b:t)for(int c:t)if(a+b+c==n)return new int[]{c,b,a};return t;}

Explanation:

Try it here.

n->{                     // Method with int parameter and int-array return-type
  int t[]=new int[n+1],  //  Create an int-array to store triangular numbers
      i=0,j=0;           //  Two index-integers
  for(;i<=n;)            //  Loop (1) from 0 to `n` (inclusive)
    if(Math.sqrt(8*i+++1)%1==0) 
                         //   If `i` is a triangular number
      t[j++]=i-1;        //    Add it to array `t`
                         //  End of for-loop (1) (implicit / single-line body)
  for(int a:t)           //  Loop (2) over the triangular numbers
    for(int b:t)         //   Inner loop (3) over the triangular numbers
      for(int c:t)       //    Inner loop (4) over the triangular numbers
        if(a+b+c==n)     //     If the three triangular numbers sum equal the input
          return new int[]{c,b,a};
                         //      Return these three triangular numbers as int-array
                         //    End of loop (4) (implicit / single-line body)
                         //   End of loop (3) (implicit / single-line body)
                         //  End of loop (2) (implicit / single-line body)
  return t;              //  Return `t` if no sum is found (Java methods always need a
                         //  return-type, and `t` is shorter than `null`;
                         //  since we can assume the test cases will always have an answer,
                         //  this part can be interpret as dead code)
}                        // End of method

Kevin Cruijssen

Posted 2017-07-17T12:44:18.640

Reputation: 67 575

0

JavaScript, 108 bytes

r=[],i=a=b=0
while(a<=x)r.push(a=i++*i/2)
for(a=0;a<3;){
b=r[i]
if(b<=x){
x-=b
a++
console.log(b)}
else i--}

Explanation

x represents the input

while(a<=x)r.push(a=i++*i/2) Creates an array of all triangular numbers up to x

The for loop prints the highest triangular number less than x, then subtracts that number from x, for three iterations. (basically a greedy algorithm)

WaffleCohn

Posted 2017-07-17T12:44:18.640

Reputation: 311

You've got the same problem I do: by taking the biggest triangle number <= x at each step, you aren't guaranteed to have a triangle number for your 3rd place. Check your output for x = 103: 91 + 10 + 1 = 102 – asgallant – 2017-07-18T16:25:09.210

0

Pyth, 19 bytes

I am so out of practise with Pyth, it's untrue :/

hfqQsT.C*3+0msSdSQ3

Try it out here.

hfqQsT.C*3+0msSdSQ3  Implicit: Q=input()

                SQ   Range 1-n
            m        Map the above over d:
              Sd       Range 1-d
             s         Sum the above
                     Yields [1,3,6,10,...]
          +0         Prepend 0 to the above
        *3           Triplicate the above
      .C          3  All combinations of 3 of the above
 f                   Filter the above over T:
    sT                 Where sum of T
  qQ                   Is equal to input
h                    Take the first element of that list

Sok

Posted 2017-07-17T12:44:18.640

Reputation: 5 592

You could possibly save a byte by leaving out the selector for the first list element because you are allowed to print all possible solutions too. – racer290 – 2017-07-17T15:56:38.040

@racer290 Even better, though the results will be in the form [[a,b,c],[d,e,f]] - would that be ok? – Sok – 2017-07-17T15:57:36.790

@racer290 Actually, no, filtering the duplicates would not be free by the looks of things, so it wouldn't be any shorter :c – Sok – 2017-07-17T15:58:45.440

0

Ruby 61 57 55 bytes

Inspired by Lynn's Python answer. It generates random triplets until the desired sum is achieved:

->n{x=Array.new 3{(0..rand(n+1)).sum}until x&.sum==n;x}

It requires Ruby 2.4. In Ruby 2.3 and older, it's a syntax error, and Range#sum isn't defined. This longer version (64 bytes) is needed for Ruby 2.3:

->n{x=Array.new(3){(a=rand(n+1))*-~a/2}until x&.inject(:+)==n;x}

Here's a small test:

f=->n{x=Array.new 3{(0..rand(n+1)).sum}until x&.sum==n;x}
# => #<Proc:0x000000018aa5d8@(pry):6 (lambda)>
f[0]
# => [0, 0, 0]
f[13]
# => [0, 3, 10]
f[5]
# => [3, 1, 1]
f[27]
# => [21, 3, 3]
f[27]
# => [0, 21, 6]
f[300]
# => [3, 21, 276]

Try it online with Ruby 2.3!

Eric Duminil

Posted 2017-07-17T12:44:18.640

Reputation: 701

0

Javascript (ES6), 108 bytes - fixed

Takes an integer as an input, outputs an array [a, b, c] containing a sorted list of triangle numbers a + b + c = x, where a is the largest triangle number less than or equal to the input, and b is the largest triangle number less than or equal to the input minus a.

x=>{t=[0],t.f=t.forEach,i=j=k=0;for(;j<x;t[i]=j+=i++);t.f(a=>t.f(b=>t.f(c=>a+b+c==x?k=[a,b,c]:0)));return k}

Explanation

x=>{
    t=[0],                               // initialize an array of triangle numbers
    t.f=t.forEach,                       // copy forEach method into t.f,
                                         // saves a net of 4 bytes
    i=j=k=0;
    for(;j<x;t[i]=j+=i++);               // populate t with all triangle numbers that
                                         // we could possibly need
    t.f(                                 // loop over all t
        a=>t.f(                          // loop over all t
            b=>t.f(                      // loop over all t
                c=>a+b+c==x?k=[a,b,c]:0  // if a+b+c = x, set k = [a,b,c], else noop
                                         // using a ternary here saves 1 byte vs
                                         // if statement
                                         // iterating over t like this will find all
                                         // permutations of [a,b,c] that match, but
                                         // we will only return the last one found,
                                         // which happens to be sorted in descending order
            )
        )
    );
    return k
}

const F = x=>{t=[0],t.f=t.forEach,i=j=k=0;for(;j<x;t[i]=j+=i++);t.f(a=>t.f(b=>t.f(c=>a+b+c==x?k=[a,b,c]:0)));return k};

$('#output').hide();
$('#run').click(() => {
  const x = parseInt($('#input').val(), 10);
  if (x >= 0) {
    $('#input').val(x);
    const values = F(x);
    $('#values').text(values.join(', '));
    $('#output').show();
  }
  else {
    $('#output').hide();
    alert('You must enter a positive integer');
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Enter a value: <input type="text" id="input" />
<br />
<button id="run">Get triangle numbers</button>
<br />
<span id="output">Triangle numbers are: <span id="values"></span></span>

asgallant

Posted 2017-07-17T12:44:18.640

Reputation: 309

You're not explaining the most interesting part: why is x-m-n a triangular number, i.e. why does this work? – Christian Sievers – 2017-07-17T23:03:38.493

Well dangit, turns out it isn't guaranteed. All of the test cases I used just happened to produce a valid triplet of triangle numbers. Back to the drawing board. – asgallant – 2017-07-17T23:44:09.363

Fixed now, less happy with this solution <;o( but at least it works. – asgallant – 2017-07-18T16:42:31.370

0

J, 36 bytes

((2!1+$@]#:0({I.@,)=)]+/+/~)2!1+i.,]

Try it online!

miles

Posted 2017-07-17T12:44:18.640

Reputation: 15 654