What comes next?

15

2

Given a space-separated list of integers, your task is to find the next integer in the sequence. Each integer in the sequence is the result of applying a single mathematical operation(+,-,* or /) to the previous integer, and each sequence is made up of a variable number of such operations (but no more than 10). No sequence will be longer than half the length of the sequence of integers, so you'll have each sequence of operations appear at least twice for confirmation.
Input will be via stdin (or prompt for JavaScript solutions).

Here are some explanatory examples.

Input:

1 3 5 7 9 11

Output:

13

Fairly easy, this one. All values are previous value +2.

Input:

1 3 2 4 3 5 4 6 5 7 6

Ouput:

8

Two steps in this sequence, +2 then -1.

Input:

2 6 7 3 9 10 6 18 19 15 45 46

Output:

42

Three steps - *3, +1, -4.

Test cases

Here are few more test cases:

Input:

1024 512 256 128 64 32 16

Output:

8

Input:

1 3 9 8 24 72 71 213 639

Output:

638

Input:

1 2 3 4 5 2 3 4 5 6 3 4 5 6 7

Output:

4

Input:

1 2 4 1 3 9 5 8 32 27 28 56 53 55 165 161 164 656 651 652 1304

Output:

1301

I have an ungolfed Scala solution (42 lines) that I will post in a couple of days.

This is code-golf - shortest answer wins.

Gareth

Posted 2011-08-18T15:46:39.270

Reputation: 11 678

Is division guaranteed to be exact? – Peter Taylor – 2011-08-18T16:25:20.240

@Peter Yes. Every step will result in an integer. – Gareth – 2011-08-18T16:26:18.300

But if the step is "/3", is it guaranteed that the remainder will always be 0? – Peter Taylor – 2011-08-18T16:46:50.813

@Peter Yes, remainder will always be 0. – Gareth – 2011-08-18T20:53:08.170

3http://oeis.org/ – Thomas Eding – 2011-08-18T21:34:25.690

It's just a cool site that I thought was related. – Thomas Eding – 2011-08-19T00:55:47.620

Are all the integers non-negative? Positive? – Thomas Eding – 2011-08-19T01:28:45.310

With your third example... how are we to decide if it's 3 steps (*3, +1, 4), or if it's 10 steps (+4,+1,-4,+6,+1,-4,+12,+1,-4,+30)?. – Rob – 2011-08-19T05:52:37.830

@Rob The question says: "No sequence will be longer than half the length of the sequence of integers, so you'll have each sequence of operations appear at least twice for confirmation." – migimaru – 2011-08-19T06:25:04.307

@trinthis All the integers in my examples are positive, but the program should be able to handle negative integers. I'll add a test with some negatives. – Gareth – 2011-08-19T07:10:33.610

It occurred to me on the bus today that there's a really evil special case which breaks my solutions. 0 0 1 2 3 6 7 14. Will work on a fix. – Peter Taylor – 2011-08-19T08:11:44.323

@Peter Wow, that is evil. It breaks my solution too. But not trinithis's though. – Gareth – 2011-08-19T08:22:54.477

Hmm, that breaks the solution I had too. I'll definitely have to rethink that one. – migimaru – 2011-08-19T08:32:34.617

Out of curiosity, what made that sequence evil? – Thomas Eding – 2011-08-19T21:38:20.147

@trinithis The first operation is *2, but you can't figure that out from 0 0 because there isn't enough information - at least that's what breaks my solution; it thinks that the first operation is +0. – Gareth – 2011-08-19T21:42:52.927

a tool to weed out Microsoft Minutes? http://www.urbandictionary.com/define.php?term=Microsoft+Minute

– Ming-Tang – 2011-08-30T01:31:52.747

Answers

12

Golfscript, 203 138 chars

~]0{).2$.);[\(;]zip\/zip{.{~-}%.&({-}+\{;{.0={.~\%{.~%{;0}{~/{/}+}if}{~\/{*}+}if}{~!{;}*}if}%.&(\{;;0}/}{\;}if}%.{!}%1?)!{0}*}do\@)\,@%@=~

This uses far more ifs than a standard Golfscript program, and its operation is pretty cryptic, so here's a commented (but not ungolfed other than by the addition of whitespace and comments) version:

~]0
# Loop over guesses L for the length of the sequence
{
    # [x0 ... xN] L-1
    ).
    # [x0 ... xN] L L
    2$.);[\(;]zip
    # [x0 ... xN] L L [[x0 x1][x1 x2]...[x{N-1} xN]]
    \/zip
    # [x0 ... xN] L [[[x0 x1][xL x{L+1}]...][[x1 x2][x{L+1} x{L+2}]...]...]
    {
        # [x0 ... xN] L [[xi x{i+1}][x{i+L} x{i+L+1}]...]
        # Is there an operation which takes the first element of each pair to the second?
        # Copy the pairs, work out each difference, and remove duplicates
        .{~-}%.&
        # Turn the first difference into an operation
        ({-}+\
        # If there was more than one unique difference, look for a multiplication
        {
            ;
            # [x0 ... xN] L [[xi x{i+1}][x{i+L} x{i+L+1}]...]
            # Do something similar, but working out multiplicative factors
            # Note that if we have [0 0] it could be multiplication by anything, so we remove it.
            # However, they can't all be [0 0] or we'd have only one unique difference
            {
                # [0     0   ] =>
                # [0     _   ] => 0     # a "false" value, because it can't possibly be multiplication
                # [a     a.b'] => {b'*}
                # [a'.b  b   ] => {a'/}
                # [_     _   ] => 0     # ditto
                # This is the obvious place to look for further improvements
                .0={.~\%{.~%{;0}{~/{/}+}if}{~\/{*}+}if}{~!{;}*}if
            }%.&
            # If we have one unique multiplication, even if it's false, keep it.
            # Otherwise discard them and replace them with false.
            (\{;;0}/
        }
        # There was only one unique difference. Discard the pairs
        {\;}if
        # operation - one of 0, {d+}, {m*}, {M/}
    }%
    # [x0 ... xN] L [op0 ... op{L-1}]
    # Is any of the operations false, indicating that we couldn't find a suitable operation?
    .{!}%1?
    # [x0 ... xN] L [op0 ... op{L-1}] idxFalse
    # If idxFalse is -1 then all the ops are ok, and we put a false to exit the loop
    # Otherwise one op failed, so we leave the array of ops, which is non-false, to be popped by the do.
    )!{0}*
}do
# [x0 ... xN] L [op0 ... op{L-1}]
\@)\,@%@=~
# op{(len(Xs)-1)%L} (Xs[-1])

My original submission was the following at 88 chars:

~]:x;2.{;).(2base(;{[{--}{@*\.!+/}]=}%[x.(;2$]zip\,<{~++}%x,.@[*x\]zip<{~~}%)\x(;=!}do\;

However, this tries to calculate the operations from the first occurrence of each, so if the operation is multiplication or division and the argument the first time round is 0 it breaks.

Peter Taylor

Posted 2011-08-18T15:46:39.270

Reputation: 41 901

1Thank you very much for posting an explanation! I've been looking for dissected Golfscript programs so I can try to make more sense of them. – migimaru – 2011-08-22T23:29:59.703

6

Haskell, 276 261 259 257 243 characters

Here is my inefficient solution. It works on unbounded (and bounded) integers. This solution happens to work correctly with non-exact division (eg: 5 / 2 = 2).

import Control.Monad
main=interact$show.n.q read.words
f=flip
q=map
_%0=0
x%y=div x y
s b=[1..]>>=q cycle.(f replicateM$[(+),(*),(%)]>>=f q[-b..b].f)
m l x s=[y!!l|y<-[scanl(f($))(x!!0)s],x==take l y]
n x=(s(maximum$q abs x)>>=m(length x)x)!!0

How it works: I create every possible sequence of (possible) operations. Then I test against the input sequence of numbers to see if the generated sequence will create the input. If it does, return the next number in the sequence. The code will always return an answer that is derived from a shortest sequence of operations. This happens because the list of operation sequences is generated in that order. It's arbitrary (but consistent) on deciding between ties. For example the code returns 6 or 8 for the sequence 2 4.

Ungolfed:

import Control.Monad

main :: IO ()
main = print . next . map read . words =<< getLine

opSequences :: Integral a => a -> [[a -> a]]
opSequences bound = [1 ..] >>= map cycle . (`replicateM` (ops >>= (`map` args) . flip))
  where
    ops = [(+), (*), \x y -> if y == 0 then 0 else div x y]
    args = [-bound .. bound]

match :: (MonadPlus m, Integral a) => [a] -> [a -> a] -> m a
match ns opSeq = guard (ns == take len ms) >> return (ms !! len)
  where
    n0 = ns !! 0
    len = length ns
    ms = scanl (flip ($)) n0 opSeq

next :: Integral a => [a] -> a
next ns = (opSequences bound >>= match ns) !! 0
  where
    bound = maximum $ map abs ns

Thomas Eding

Posted 2011-08-18T15:46:39.270

Reputation: 796

Nice idea for how to handle non-exact division. – Peter Taylor – 2011-08-19T05:40:47.607

It was an accidental side-effect. Searching for a solution was just my initial idea on how to solve this problem, as to me, it felt like a simpler task than computing the answer. – Thomas Eding – 2011-08-19T07:58:13.073

Is Control.Monad -> Monad possible? And how about interact$show.n.q read.words – FUZxxl – 2011-08-20T11:40:10.697

6

Python, 333 366 ... 315 303 278 269 261 246 chars

n=map(float,raw_input().split())
y=len(n)-1
l=1
def O(f):
 p,q=n[f:y:l],n[f+1::l]
 for a,b in zip(p,q):
    for x in((b-a).__add__,(b/(a or 1)).__mul__):
     if map(x,p)==q:return x
while 1:
 if all(map(O,range(l))):print int(O(y%l)(n[y]));break
 l+=1

Creates operation with first pair of numbers and check it on other pairs. Stores all operations, and if all of them succeed then applies appropriate operation on list last element.

Edited: passes evil test:-) Now search for operation on all positions.

Ante

Posted 2011-08-18T15:46:39.270

Reputation: 321

Nice. Passes all my tests, but not Peter Taylor's particularly evil one: 0 0 1 2 3 6 7 14 – Gareth – 2011-08-19T09:29:23.360

0 0 0 0 1 0 0 0 0 1 does not output 0. – Thomas Eding – 2011-08-19T20:51:06.260

@trinithis That input doesn't conform to the spec. The sequence of operations must repeat completely at least once. – Gareth – 2011-08-19T21:11:42.150

Oh, I see. It visually tricked me :D (visually it repeats) – Thomas Eding – 2011-08-19T21:36:22.957

@Ante, you don't actually have to limit that last for loop: for l in r(y): works just fine for all valid inputs. – Clueless – 2011-08-21T22:31:39.587

@Clueless, thank you, now it will print result even for some non-valid inputs like trinithis's, but that is not specified in question :-) – Ante – 2011-08-22T05:08:27.550

Knocked off a few chars for you: http://pastie.org/2415748 .

– Clueless – 2011-08-23T08:42:35.893

1Oooh, here's a fun improvement: lambda x:x+b-a --> (b-a).__add__. Too bad it's only one character, I'm learning so much about python by doing these. – Clueless – 2011-08-23T09:17:58.753

@Clueless, tnx. add is very good idea. There are always now things to learn :-) I'm also very glad to get rid of r=range line, it wasn't nice at all :-) – Ante – 2011-08-23T10:09:02.080

You can do the same trick for the other operation if you use floats as intermediate values throughout: http://pastie.org/2416367

– Clueless – 2011-08-23T11:44:23.913

1

Making l implicitly global saves a lot: http://pastie.org/2416407

– Clueless – 2011-08-23T11:54:56.227

@Clueless, you changed a lot: l, floats, mul, map(). Can I transfer answer to you? :-) – Ante – 2011-08-23T12:55:21.840

4

Python, 309 305 295 279 chars

Handles all of the original test cases, as well as Peter Taylor's gnarly 0 0 1 2 3 6 7 14 one:

l=map(int,raw_input().split())
i=v=0
while v<1:
 v=i=i+1
 for j in range(i):
    p=zip(l[j::i],l[j+1::i])
    d,r=set(q[1]-q[0]for q in p),set(q[1]*1./(q[0]or 1)for q in p if any(q))
    m,n=len(d)<2,len(r)<2
    v*=m+n
    if(len(l)-1)%i==j:s=l[-1]+d.pop()if m else int(l[-1]*r.pop())
print s

Ungolfed, with debugging output (very helpful in verifying correctness):

nums = map(int,raw_input().split())
result = None

for i in range(1,len(nums)/2+1):
    print "-- %s --" % i
    valid = 1
    for j in range(i):
        pairs = zip(nums[j::i], nums[j+1::i])
        print pairs

        diffs = [pair[1] - pair[0] for pair in pairs]
        # this is the tough bit: (3, 6) -> 2, (0, 5) -> 5, (5, 0) -> 0, (0, 0) ignored
        ratios = [float(pair[1])/(pair[0] or 1) for pair in pairs if pair[0] != 0 or pair[1] != 0]

        if len(set(diffs))==1:
            print "  can be diff", diffs[0]
            if (len(nums) - 1) % i == j:
                result = nums[-1] + diffs.pop()
        elif len(set(ratios))==1:
            print "  can be ratio", ratios[0]
            if (len(nums) - 1) % i == j:
                result = int(nums[-1]*ratios.pop())
        else:
            print "** invalid period **"
            valid=0
    if valid and result is not None:
        break

print result

Usage:

echo 0 0 1 2 3 6 7 14 | python whatcomesnext.py

Clueless

Posted 2011-08-18T15:46:39.270

Reputation: 710

I've upvoted, though strictly speaking the input should be via stdin rather than command arguments. – Gareth – 2011-08-21T22:06:09.933

That actually lets me shorten the program by four characters :) – Clueless – 2011-08-21T22:29:57.967

Few chars, i=v=0 and while v==0: – Ante – 2011-08-22T08:57:01.400

@Ante thanks, I thought you couldn't do that in python because assignment isn't an expression, but it's a helpful tidbit for golfing. Literal tabs as the second level indentation also helps. – Clueless – 2011-08-23T08:11:32.760

I'm not a Pythoner, but you seem to be using booleans as integers in some expressions, and yet can't use integer v as a boolean in the while test. Is that right? If so, surely v<1 works as a guard. – Peter Taylor – 2011-08-23T08:28:54.783

@Peter Yep, see http://stackoverflow.com/questions/7134984/why-does-1-true-but-2-true-in-python which arose in part from this code golf. I will add your improvement if and when I can get some more of my own; making an edit and another strikethrough for one character seems petty :)

– Clueless – 2011-08-23T09:06:31.207

I've just tested and it seems that while v: should work. I'll happily edit for one character, but for 3... And you can get a few more too. v*= could be v= because the only thing you care about is zero or not. And there's some potential saving around comparing the lengths of d and r to 1 - at the very least you can make those 1== into 2>.

– Peter Taylor – 2011-08-23T09:40:58.273

The while v==0: statement is there to repeat until all of the iterations through the inner loop are valid. I don't think I can change v*= to v= because then it would only consider the last time through the inner loop. You're definitely right about the other =='s though, they can also change. – Clueless – 2011-08-23T10:21:01.303

3

Ruby 1.9 (437) (521) (447) (477)

Works for all test cases, including the "evil" one. I'll golf it more later.

EDIT: I realized there's another case that I wasn't handling properly - when the continuation needs to use the "mystery" operation. The sequence 2 0 0 -2 -4 -6 was initially returning 0 instead of -12. I've now fixed that.

EDIT: Fixed a couple more edge cases and cut down the code to 447.

EDIT: Ugh. Had to add some code to handle other "evil" sequences such as 0 0 0 6 18 6 12

def v(c,q);t=*q[0];q.size.times{|i|f=c[z=i%k=c.size]
f=c[z]=(g=q[z+k])==0??_:((h=q[z+k+1])%g==0?"*(#{h/g})":"/(#{g/h})")if f==?_
t<<=f==?_?(a=q[i];b=q[i+1].nil?? 0:q[i+1];(a==0&&b==0)||(a!=0&&(b%a==0||a%b==0))?b:0):eval(t.last.to_s+f)}
*r,s=t
(p s;exit)if q==r
end
s=gets.split.map &:to_i
n=[[]]
((s.size-1)/2).times{|i|a,b=s[i,2]
j=["+(#{b-a})"]
j<<=?_ if a==0&&b==0
j<<="*#{b/a}"if a!=0&&b%a==0
j<<="/#{a/b}"if a*b!=0&&a%b==0
n=n.product(j).map(&:flatten)
n.map{|m|v(m*1,s)}}

migimaru

Posted 2011-08-18T15:46:39.270

Reputation: 1 040

1

Scala

This is the solution I came up with:

object Z extends App{var i=readLine.split(" ").map(_.toInt)
var s=i.size
var(o,v,f)=(new Array[Int](s),new Array[Double](s),1)
def d(p:Int,j:Array[Int]):Unit={if(p<=s/2){def t(){var a=new Array[Int](s+1)
a(0)=i(0)
for(l<-0 to s-1){o(l%(p+1))match{case 0=>a(l+1)=a(l)+v(l%(p+1)).toInt
case _=>a(l+1)=(a(l).toDouble*v(l%(p+1))).toInt}}
if(a.init.deep==i.deep&&f>0){f^=f
println(a.last)}}
o(p)=0 
v(p)=j(1)-j(0)
t
d(p+1,j.tail)
o(p)=1
v(p)=j(1).toDouble/j(0).toDouble
t
d(p+1,j.tail)}}
d(0,i)
i=i.tail
d(0,i)}

Ungolfed:

object Sequence extends App
{
    var input=readLine.split(" ").map(_.toInt)
    var s=input.size
    var (ops,vals,flag)=(new Array[Int](s),new Array[Double](s),1)
    def doSeq(place:Int,ints:Array[Int]):Unit=
    {
        if(place<=s/2) 
        {
            def trysolution()
            {
                var a=new Array[Int](s+1)
                a(0)=input(0)
                for(loop<-0 to s-1)
                {
                    ops(loop%(place+1))match
                    {
                        case 0=>a(loop+1)=a(loop)+vals(loop%(place+1)).toInt
                        case _=>a(loop+1)=(a(loop).toDouble*vals(loop%(place+1))).toInt
                    }
                }
                if(a.init.deep==input.deep&&flag>0)
                {
                    flag^=flag
                    println(a.last)
                }
            }
            ops(place)=0
            vals(place)=ints(1)-ints(0)
            trysolution
            doSeq(place+1,ints.tail)
            ops(place)=1
            vals(place)=ints(1).toDouble/ints(0).toDouble
            trysolution
            doSeq(place+1,ints.tail)
        }
    }
    doSeq(0,input)
    input=input.tail
    doSeq(0,input)
}

Gareth

Posted 2011-08-18T15:46:39.270

Reputation: 11 678

How do I invoke it? echo "0 0 1 2 3 6 7 14" | scala Sequence keeps the screen black. – user unknown – 2011-08-22T17:32:02.133

@user unknown scala Sequence and then enter the sequence and press enter. – Gareth – 2011-08-22T21:23:09.507

Ah, you wrote in the questions comment, that you don't solve this specific one - it works with echo - pipe as above for the solvable questions. – user unknown – 2011-08-22T22:16:34.893

1

Scala 936

type O=Option[(Char,Int)]
type Q=(O,O)
type L=List[Q]
val N=None
def t(a:Int,b:Int):Q=if(a>b)(Some('-',a-b),(if(b!=0&&b*(a/b)==a)Some('/',a/b)else N))else
(Some('+',b-a),(if(a!=0&&a*(b/a)==b)Some('*',b/a)else N))
def w(a:Q,b:Q)=if(a._1==b._1&&a._2==b._2)a else
if(a._1==b._1)(a._1,N)else
if(a._2==b._2)(N,a._2)else(N,N)
def n(l:L):Q=l match{case Nil=>(N,N)
case x::Nil=>x
case x::y::Nil=>w(x,y)
case x::y::xs=>n(w(x,y)::xs)} 
def z(l:L,w:Int)=for(d<-1 to w)yield
n(l.drop(d-1).sliding(1,w).flatten.toList)
def h(s:L):Boolean=s.isEmpty||(s(0)!=(N,N))&& h(s.tail)
def j(s:L,i:Int=1):Int=if(h(z(s,i).toList))i else j(s,i+1)
def k(b:Int,o:Char,p:Int)=o match{case'+'=>b+p
case'-'=>b-p
case'*'=>b*p
case _=>b/p}
val e=getLine 
val i=e.split(" ").map(_.toInt).toList
val s=i.sliding(2,1).toList.map(l=>t(l(0),l(1)))
val H=n(s.drop(s.size%j(s)).sliding(1,j(s)).flatten.toList)
val c=H._1.getOrElse(H._2.get)
println (k(i(i.size-1),c._1,c._2))

ungolfed:

type O = Option[(Char, Int)]

def stepalize (a: Int, b: Int): (O, O) = (a > b) match {
   case true => (Some('-', a-b), (if (b!=0 && b * (a/b) == a) Some ('/', a/b) else None)) 
   case false=> (Some('+', b-a), (if (a!=0 && a * (b/a) == b) Some ('*', b/a) else None)) }

def same (a: (O, O), b: (O, O)) = {
  if (a._1 == b._1 && a._2 == b._2) a else
  if (a._1 == b._1) (a._1, None) else 
  if (a._2 == b._2) (None, a._2) else 
  (None, None)}

def intersection (lc: List[(O, O)]): (O, O) = lc match {
  case Nil => (None, None)
  case x :: Nil => x
  case x :: y :: Nil => same (x, y)
  case x :: y :: xs  => intersection (same (x, y) :: xs)} 

def seriallen (lc: List[(O, O)], w: Int= 1) =
  for (d <- 1 to w) yield 
    intersection (lc.drop (d-1).sliding (1, w).flatten.toList)

def hit (s: List[(O, O)]) : Boolean = s match {
  case Nil => true 
  case x :: xs => (x != (None, None)) && hit (xs)}

def idxHit (s: List[(O, O)], idx: Int = 1) : Int =
  if (hit (seriallen (s, idx).toList)) idx else 
    idxHit (s, idx+1)

def calc (base: Int, op: Char, param: Int) = op match {
  case '+' => base + param
  case '-' => base - param
  case '*' => base * param
  case _   => base / param}

def getOp (e: String) = {
  val i = e.split (" ").map (_.toInt).toList
  val s = i.sliding (2, 1).toList.map (l => stepalize (l(0), l(1)))
  val w = idxHit (s)
  val hit = intersection (s.drop (s.size % w).sliding (1, w).flatten.toList)
  val ci = hit._1.getOrElse (hit._2.get)
  val base = i(i.size - 1)
  println ("i: " + i + " w: " + w + " ci:" + ci + " " + calc (base, ci._1, ci._2))
}

val a="1 3 5 7 9 11"
val b="1 3 2 4 3 5 4 6 5 7 6"
val c="2 6 7 3 9 10 6 18 19 15 45 46"
val d="1024 512 256 128 64 32 16"
val e="1 3 9 8 24 72 71 213 639"
val f="1 2 3 4 5 2 3 4 5 6 3 4 5 6 7"
val g="1 2 4 1 3 9 5 8 32 27 28 56 53 55 165 161 164 656 651 652 1304"
val h="0 0 1 2 3 6 7 14"
val i="0 0 0 0 1 0 0 0 0 1 0"

List (a, b, c, d, e, f, g, h, i).map (getOp)

Fails miserably on Peter Taylor's h, but I don't see the possibility to heal the program in a reasonable amount of time.

user unknown

Posted 2011-08-18T15:46:39.270

Reputation: 4 210

Would it help to shrink it if you treated - as a special case of + and / as a special case of *? My way of passing Peter Taylor's (and similar) input was to chop off the first number in the sequence and try again. I haven't had time to look at how your program works yet to know if that would help with yours. – Gareth – 2011-08-22T17:25:53.007

I guess it would help, but only for that special case. A series which contains the null-multiplication later, like -1, 0, 0, 1, 2, 3, 6, 7, 14 will need a different healing. – user unknown – 2011-08-22T19:26:31.240