27
\$\begingroup\$

Overview

Some of you might be aware of the Kolakoski Sequence (A000002), a well know self-referential sequence that has the following property:

The coolio Kolakoski property, yo.

It is a sequence containing only 1's and 2's, and for each group of 1's and twos, if you add up the length of runs, it equals itself, only half the length. In other words, the Kolakoski sequence describes the length of runs in the sequence itself. It is the only sequence that does this except for the same sequence with the initial 1 deleted. (This is only true if you limit yourself to sequences made up of 1s and 2s - Martin Ender)


The Challenge

The challenge is, given a list of integers:

  • Output -1 if the list is NOT a working prefix of the Kolakoski sequence.
  • Output the number of iterations before the sequence becomes [2].

The Worked Out Example

Using the provided image as an example:

[1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1] # Iteration 0 (the input).
[1,2,2,1,1,2,1,2,2,1,2]             # Iteration 1.
[1,2,2,1,1,2,1,1]                   # Iteration 2.
[1,2,2,1,2]                         # Iteration 3.
[1,2,1,1]                           # Iteration 4.
[1,1,2]                             # Iteration 5.
[2,1]                               # Iteration 6.
[1,1]                               # Iteration 7.
[2]                                 # Iteration 8.

Therefore, the resultant number is 8 for an input of [1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1].

9 is also fine if you are 1-indexing.


The Test Suite (You can test with sub-iterations too)

------------------------------------------+---------
Truthy Scenarios                          | Output
------------------------------------------+---------
[1,1]                                     | 1 or 2
[1,2,2,1,1,2,1,2,2,1]                     | 6 or 7
[1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1]       | 8 or 9
[1,2]                                     | 2 or 3
------------------------------------------+---------
Falsy Scenarios                           | Output
------------------------------------------+---------
[4,2,-2,1,0,3928,102904]                  | -1 or a unique falsy output.
[1,1,1]                                   | -1
[2,2,1,1,2,1,2] (Results in [2,3] @ i3)   | -1 (Trickiest example)
[]                                        | -1
[1]                                       | -1

If you're confused:

Truthy: It will eventually reach two without any intermediate step having any elements other than 1 and 2. – Einkorn Enchanter 20 hours ago

Falsy: Ending value is not [2]. Intermediate terms contain something other than something of the set [1,2]. A couple other things, see examples.


This is , lowest byte-count will be the victor.

\$\endgroup\$
  • 7
    \$\begingroup\$ Can we use any falsey value instead of just -1? \$\endgroup\$ – mbomb007 Jun 28 '17 at 20:05
  • 1
    \$\begingroup\$ What do you mean by "NOT a working prefix of the Kolakoski sequence"? I had assumed you meant the list does not eventually reach [2] until I saw the [2,2,1,1,2,1,2] test case. \$\endgroup\$ – ngenisis Jun 28 '17 at 21:42
  • 1
    \$\begingroup\$ @ngenisis It will eventually reach two without any intermediate step having any elements other than 1 and 2. \$\endgroup\$ – Sriotchilism O'Zaic Jun 29 '17 at 0:27
  • 2
    \$\begingroup\$ Might be a good idea to add [1] as a test case. \$\endgroup\$ – Emigna Jun 29 '17 at 9:54
  • 1
    \$\begingroup\$ @mbomb007 any distinct value is fine. A positive integer is not fine. If you're 1-indexing 0 is fine. "False" is fine. Erroring is fine. Any non-positive return value is fine, even -129.42910. \$\endgroup\$ – Magic Octopus Urn Jun 29 '17 at 15:33

16 Answers 16

8
\$\begingroup\$

Haskell, 126 87 79 76 75 bytes

39 bytes saved thanks to Ørjan Johansen

import Data.List
f[2]=0
f y@(_:_:_)|all(`elem`[1,2])y=1+f(length<$>group y)

Try it online!

This errors on bad input.

\$\endgroup\$
  • \$\begingroup\$ f (and consequently !) can be shortened a lot by using lazy production + span/length instead of accumulators. Try it online! \$\endgroup\$ – Ørjan Johansen Jun 29 '17 at 2:41
  • 1
    \$\begingroup\$ Seem to enter an infinite loop for [1] \$\endgroup\$ – Emigna Jun 29 '17 at 9:59
  • 1
    \$\begingroup\$ @Emigna Darn. It costs me 6 bytes to fix it, but I've fixed it. \$\endgroup\$ – Sriotchilism O'Zaic Jun 29 '17 at 14:32
  • \$\begingroup\$ @ØrjanJohansen That seems like a good tip, but I'm not proficient enough in Haskell to understand whats going on there. If you want you can post it as your own answer but at least as long as I don't know how your solution works, I'm not going to add it to my answer. :) \$\endgroup\$ – Sriotchilism O'Zaic Jun 29 '17 at 14:36
  • 1
    \$\begingroup\$ I then realized this is a case where an import is actually shorter (and also simpler to understand): import Data.List;f l=length<$>group l. (<$> is a synonym for map here.) Also, instead of having two different -1 cases it is shorter to use a @(_:_:_) pattern to force the main case to only match length >=2 lists. Try it online! \$\endgroup\$ – Ørjan Johansen Jun 30 '17 at 0:44
6
\$\begingroup\$

05AB1E, 22 bytes

[Dg2‹#γ€gM2›iX]2QJiNë®

Try it online!

Explanation

[                        # start a loop
 D                       # duplicate current list
  g2‹#                   # break if the length is less than 2
      γ                  # group into runs of consecutive equal elements
       €g                # get length of each run
         M2›iX           # if the maximum run-length is greater than 2, push 1
              ]          # end loop
               2QJi      # if the result is a list containing only 2
                   N     # push the iteration counter from the loop
                    ë®   # else, push -1
                         # implicitly output top of stack
\$\endgroup\$
  • \$\begingroup\$ Fails for [1,1,2,2,1,2,1,1,2,2,1,2,2,1,1,2,1,1] \$\endgroup\$ – Weijun Zhou Mar 24 '18 at 10:04
  • \$\begingroup\$ @WeijunZhou: Thanks, fixed! \$\endgroup\$ – Emigna Mar 24 '18 at 10:50
  • \$\begingroup\$ You may have forgotten to update the link ... \$\endgroup\$ – Weijun Zhou Mar 24 '18 at 10:56
  • 1
    \$\begingroup\$ @WeijunZhou: Indeed I had. Thanks again :) \$\endgroup\$ – Emigna Mar 24 '18 at 10:58
3
\$\begingroup\$

SCALA, 290(282?) chars, 290(282?) bytes

It took me sooo loong ... But I'm finally done!

with this code :

var u=t
var v=Array[Int]()
var c= -1
var b=1
if(!u.isEmpty){while(u.forall(x=>x==1|x==2)){c+=1
if(u.size>1){var p=u.size-1
for(i<-0 to p){if(b==1){var k=u(i)
v:+=(if(i==p)1 else if(u(i+1)==k){b=0
if(p-i>1&&u(i+2)==k)return-1
2}else 1)} else b=1}
u=v
v=v.take(0)}else if(u(0)==2)return c}}
c

I don't know if I should count the var u=t into the bytes, considering I do not use t during the algorithm (the copy is just to get a modifyable var instead of the parameter t considered as val - thanks ScaLa). Please tell me if I should count it.

Hard enough. Try it online!

PS : I was thinking of doing it recursively, but I'll have to pass a counter as a parameter of the true recursive "subfunction" ; this fact makes me declare two functions, and these chars/bytes are nothing but lost.

EDIT : I had to change (?) because we're not sure we should take in count [1] case. So here is the modified code :

var u=t
var v=Array[Int]()
var c= -1
var b=1
if(!u.isEmpty){try{t(1)}catch{case _=>return if(t(0)==2)0 else -1}
while(u.forall(x=>x==1|x==2)){c+=1
if(u.size>1){var p=u.size-1
for(i<-0 to p){if(b==1){var k=u(i)
v:+=(if(i==p)1 else if(u(i+1)==k){b=0
if(p-i>1&&u(i+2)==k)return-1
2}else 1)} else b=1}
u=v
v=v.take(0)}else if(u(0)==2)return c}}
c

It's not optimized (I have a duplicate "out" for the same conditions : when I get to [2] and when param is [2] is treated separatedly).

NEW COST = 342 (I didn't modify the title on purpose)

\$\endgroup\$
  • 1
    \$\begingroup\$ Seem to enter an infinite loop for [1] \$\endgroup\$ – Emigna Jun 29 '17 at 10:01
  • \$\begingroup\$ Yep, but as said by the OP (as I understood at least) : "with the initial 1 deleted" and "Output the number of iterations before the sequence becomes [2]" \$\endgroup\$ – V. Courtois Jun 29 '17 at 10:03
  • \$\begingroup\$ To my understanding, [1] never reaches [2] and should thus return -1. \$\endgroup\$ – Emigna Jun 29 '17 at 10:06
  • \$\begingroup\$ I see. So do you think I should put a litte condition at start? Thanks for your advice. \$\endgroup\$ – V. Courtois Jun 29 '17 at 10:09
  • \$\begingroup\$ I don't know scala but I assume you can just modify the loop to stop when the length of the list is smaller than 2. You already seem to have the check that the element is 2 at the end. \$\endgroup\$ – Emigna Jun 29 '17 at 10:11
2
\$\begingroup\$

JavaScript, 146 142 bytes

First try in code golfing, it seems that the "return" in the larger function is quite tedious...

Also, the checking of b=1 and b=2 takes up some bytes...

Here's the code:

f=y=>{i=t=!y[0];while(y[1]){r=[];c=j=0;y.map(b=>{t|=b-1&&b-2;if(b-c){if(j>0)r.push(j);c=b;j=0}j++});(y=r).push(j);i++}return t||y[0]-2?-1:0^i}

Explanation

f=y=>{/*1*/}                                        //function definition

//Inside /*1*/:
  i=t=!y[0];                                        //initialization
                                                    //if the first one is 0 or undefined, 
                                                    //set t=1 so that it will return -1   
                                                    //eventually, otherwise i=0
  while(y[1]){/*2*/}                                //if there are 2+ items, start the loop

  //Inside /*2*/:
    r=[];c=j=0;                                     //initialization
    y.map(b=>{/*3*/});                              //another function definition

    //Inside /*3*/:
      t|=b-1&&b-2;                                  //if b==1 or b==2, set t=1 so that the
                                                    //entire function returns -1
      if(b-c){if(j>0)r.push(j);c=b;j=0}             //if b!=c, and j!=0, then push the 
                                                    //count to the array and reset counter
      j++                                           //counting duplicate numbers

    (y=r).push(j);i++                               //push the remaining count to the array
                                                    //and proceed to another stage

  return t||y[0]-2?-1:0^i                           //if the remaining element is not 2, or
                                                    //t==1 (means falsy), return -1,
                                                    //otherwise return the counter i

Test data (using the given test data)

l=[[1,1],[1,2,2,1,1,2,1,2,2,1],[1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1],[1,2],[4,2,-2,1,0,3928,102904],[1,1,1],[2,2,1,1,2,1,2],[]];
console.log(l.map(f));
//Output: (8) [1, 6, 8, 2, -1, -1, -1, -1]

Edit 1: 146 -> 142: Revoking my edit on reducing bytes, because this affects the output; and some edit on the last statement

\$\endgroup\$
  • \$\begingroup\$ f=a=>{for(i=t=!a[0];a[1];)r=[],c=j=0,a.map(a=>{t|=a-1&&a-2;a-c&&(0<j&&r.push(j),c=a,j=0);j++}),(a=r).push(j),i++;return t||a[0]-2?-1:0^i} saves 5 bytes (for loop instead of while; commas vs braces; && vs if). You can use google's closure compiler (closure-compiler.appspot.com) to get these optimisations done for you \$\endgroup\$ – Oki Jun 29 '17 at 12:05
2
\$\begingroup\$

Jelly, 26 25 22 21 20 bytes

FQœ-2R¤
ŒgL€µÐĿṖ-LÇ?

Try it online!

This code actually wasn't working correctly until 20 bytes and I didn't even notice; it was failing on the [2,2] test case. Should work perfectly now.

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 127 126 95 80 bytes

g=(a,i,p,b=[])=>a.map(x=>3>x&0<x?(x==p?b[0]++:b=[1,...b],p=x):H)==2?i:g(b,~~i+1)

0-indexed. Throws "ReferenceError: X is not defined" and "InternalError: too much recursion" on bad input.

Test cases

g=(a,i,p,b=[])=>a.map(x=>3>x&0<x?(x==p?b[0]++:b=[1,...b],p=x):H)==2?i:g(b,~~i+1)

function wrapper(testcase) {
  try {console.log(g(testcase))} catch(e) {
    console.log("Error")
  }
}

wrapper([1,1])
wrapper([1,2,2,1,1,2,1,2,2,1])
wrapper([1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1])
wrapper([1,2])
wrapper([4,2,-2,1,0,3928,102904])
wrapper([1,1,1])
wrapper([2,2,1,1,2,1,2])
wrapper([])
wrapper([1])

\$\endgroup\$
1
\$\begingroup\$

Clojure, 110 bytes

#(if-not(#{[][1]}%)(loop[c % i 0](if(every? #{1 2}c)(if(=[2]c)i(recur(map count(partition-by + c))(inc i))))))

A basic loop with a pre-check on edge cases. Returns nil for invalid inputs. I did not know (= [2] '(2)) is true :o

\$\endgroup\$
1
\$\begingroup\$

Python 2, 146 bytes (function only)

f=lambda l,i=0:i if l==[1]else 0if max(l)>2or min(l)<1else f([len(x)+1for x in"".join(`v!=l[i+1]`[0]for i,v in enumerate(l[:-1])).split("T")],i+1)

Returns 0 on falsy input (ok since it's 1-indexed). Simply use it like this:

print(f([1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1]))
\$\endgroup\$
1
\$\begingroup\$

Mathematica, 82 bytes

FixedPointList[#/.{{2}->T,{(1|2)..}:>Length/@Split@#,_->0}&,#]~FirstPosition~T-1&

Function which repeatedly replaces {2} with the undefined symbol T, a list of (one or more) 1s and 2s with the next iteration, and anything else with 0 until a fixed point is reached, then returns the FirstPosition of the symbol T in the resulting FixedPointList minus 1. Output is {n} where n is the (1-indexed) number of iterations needed to reach {2} for the truthy case and -1+Missing["NotFound"] for the falsy case.

If the output must be n rather than {n}, it costs three more bytes:

Position[FixedPointList[#/.{{2}->T,{(1|2)..}:>Length/@Split@#,_->0}&,#],T][[1,1]]-1&
\$\endgroup\$
1
\$\begingroup\$

Python 2, 184 163 156 bytes

  • @Felipe Nardi Batista saved 21 bytes!!!! thanks a lot!!!!
  • Halvard Hummel saved 7 bytes!! thanks

Python 2, 156 bytes

a,c=input(),0
t=a==[]
while 1<len(a)and~-t:
 r,i=[],0
 while i<len(a):
	j=i
	while[a[j]]==a[i:i+1]:i+=1
	r+=[i-j]
 a=r;c+=1;t=any(x>2for x in a)
print~c*t+c

Try it online!

Explanation:

a,c=input(),0                             #input and initialize main-counter 

t=a==[]                                   #set t to 1 if list's empty. 

while len(a)>1:                           #loop until list's length is 1.

 r,i=[],0                                 #Initialize temp. list and 
                                          #list-element-pointer 

 while i<len(a):                          #loop for the element in list 

  j=0                                     #set consecutive-item-counter to 0   

  while(i+j)<len(a)and a[i]==a[i+j]:j+=1  #increase the consec.-counter

  r+=[j];i+=j                             #add the value to a list, move the 
                                          #list-element-pointer 

 a=r;c+=1;t=any(x>2for x in a)            #update the main list, increase t 
                                          #the counter, check if any number 
 if t:break;                              #exceeds 2 (if yes, exit the loop)

print[c,-1][t]                            #print -1 if t or else the 
                                          #counter's 
                                          #value 
\$\endgroup\$
1
\$\begingroup\$

Jelly, 17 bytes

ŒgL€$ÐĿµẎḟ1,2ȯL_2

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Python 2, 122 bytes

def f(s,c=2,j=0):
 w=[1]
 for i in s[1:]:w+=[1]*(i!=s[j]);w[-1]+=i==s[j];j+=1
 return(w==[2])*c-({1,2}!=set(s))or f(w,c+1)

Try it online!

Python 3, 120 bytes

def f(s,c=2,j=0):
 w=[1]
 for i in s[1:]:w+=[1]*(i!=s[j]);w[-1]+=i==s[j];j+=1
 return(w==[2])*c-({1,2}!={*s})or f(w,c+1)

Try it online!

Explanation

A new sequence (w) is initialized to store the next iteration of the reduction. A counter (c) is initalized to keep track of the number of iterations.

Every item in the original sequence (s) is compared to the previous value. If they are the same, the value of the last item of (w) is increased with 1. If they are different, the sequence (w) is extended with [1].

If w==[2], the counter (c) is returned. Else, if the original sequence (s) contains other items than 1 and 2, a value -1 is returned. If neither is the case, the function is called recursively with the new sequence (w) as (s) and the counter (c) increased by 1.

\$\endgroup\$
  • \$\begingroup\$ To save a byte, I'm trying to combine the first two lines into def f(s,c=2,j=0,w=[1]):, but that gives a different result. Could anybody explain why that is? \$\endgroup\$ – Jitse Jul 24 at 8:14
  • 1
    \$\begingroup\$ Mutable default arguments will stay mutated \$\endgroup\$ – Jo King Jul 24 at 11:02
  • \$\begingroup\$ @JoKing That makes perfect sense, thanks! \$\endgroup\$ – Jitse Jul 24 at 11:05
0
\$\begingroup\$

R, 122 bytes

a=scan()
i=0
f=function(x)if(!all(x%in%c(1,2)))stop()
while(length(a)>1){f(a)
a=rle(a)$l
f(a)
i=i+1}
if(a==2)i else stop()

Passes all test cases. Throws one or more errors otherwise. I hate validity checks; this code could have been so golfed if the inputs were nice; it would be shorter even in case the input were a sequence of 1’s and 2’s, not necessarily a prefix of the Kolakoski sequence. Here, we have to check both the initial vector (otherwise the test case [-2,1]) would have passed) and the resulting vector (otherwise [1,1,1] would have passed).

\$\endgroup\$
0
\$\begingroup\$

Ruby, 81 77 bytes

f=->a,i=1{a[1]&&a-[1,2]==[]?f[a.chunk{|x|x}.map{|x,y|y.size},i+1]:a==[2]?i:0}

Try it online!

Edit: Saved 4 bytes by converting to recursive lambda.

Returns 1-indexed number of iterations or 0 as falsey.

Makes use of Ruby enumerable's chunk method, which does exactly what we need - grouping together consecutive runs of identical numbers. The lengths of the runs constitute the array for the next iteration. Keeps iterating while the array is longer than 1 element and no numbers other than 1 and 2 have been encountered.

\$\endgroup\$
0
\$\begingroup\$

Pyth, 45 bytes

L?||qb]1!lb-{b,1 2_1?q]2b1Z.V0IKy~QhMrQ8*KhbB

Try it online!

This is probably still golfable. It's definitely golfable if .? worked the way I hoped it would (being an else for the innermost structure instead of the outermost)

L?||qb]1!lb-{b,1 2_1?q]2b1Z # A lambda function for testing an iteration of the shortening
L                           # y=lambda b:
 ?                          # if
    qb]1                    #    b == [1]
   |    !lb                 #      or !len(b)
  |         {b              #        or b.deduplicate()
           -  ,1 2          #             .difference([1,2]):
                  _1        #               return -1
                    ?q]2b1Z # else: return 1 if [2] == b else Z (=0)

.V0                         # for b in range(0,infinity):
   IKy~Q                    # if K:=y(Q :=        (applies y to old value of Q)
        hM                  #    map(_[0],
          rQ8               #               run_length_encode(Q)):
             *Khb           #    print(K*(b+1))
                 B          #    break
\$\endgroup\$
0
\$\begingroup\$

Perl 5 -p, 71 bytes

$_.=$";s/(. )\1*/$&=~y|12||.$"/ge&$.++while/^([12] ){2,}$/;$_=/^2 $/*$.

Try it online!

1-indexed. Outputs 0 for falsy.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.