When in the Fibonacci number recurrence fn+2=fn+fn+1 one replaces the "+" with string concatenation one will get the sequence of Fibonacci words. If we start with F0=a and F1=b, then the first few elements will bea,b,ab,bab,abbab,bababbab,abbabbababbab,... etc.
If N>1 and n<N then Fn will naturally occur as a substring in FN:
For example, if n=3 and N=6, Then F4, by construction, contains a copy of F3 at its end. F5, being the concatenation of F3 and F4, therefore contains two copies, and F6 three copies, one from F4 and two from F5.We can find these copies at offsets 2,5 and 10:
ababbab-->abbab -->bababbab--> -->abbabbababbab -->--> -->But, we cannot rule out more occurrences, for example, there is one at offset 7 that is not directly explained by the recursion formula:
abbabbababbab ***We'll call such occurrences "strange" as opposed to the "regular" occurrences above.
Task:
Given N>n and offset o your program or function must return one out of three consistent values indicating whether Fn occurs in FN at offset o and if so which kind of occurrence it is.
Test cases:
N n o answer------------------------------------------4 0 0 regular4 0 1 wrong4 0 2 wrong4 0 3 regular4 0 4 wrong5 0 0 wrong5 0 1 regular5 0 2 wrong5 0 3 regular5 0 4 wrong5 0 5 wrong5 0 6 regular5 0 7 wrong4 1 0 wrong4 1 1 regular4 1 2 regular4 1 3 wrong4 1 4 regular5 1 0 regular5 1 1 wrong5 1 2 regular5 1 3 wrong5 1 4 regular5 1 5 regular5 1 6 wrong5 1 7 regular1 0 0 wrong10 2 7 wrong7 3 0 regular7 3 7 strange7 3 9 wrong7 3 10 regular7 3 11 wrong14 7 47 strange14 7 479 strange14 7 335 strange14 7 335 strange14 7 369 strange14 7 513 strange14 7 100 wrong14 7 200 wrong14 7 300 wrong14 7 13 regular14 7 123 regular14 7 233 regular14 7 322 regularIf you need even more test caseshere is the Python script that generated the above.
Inspired by this puzzling SE post:
- 1\$\begingroup\$Suggested edge case:
2 0 0(or anything else with \$n=0\$).\$\endgroup\$Arnauld– Arnauld2024-09-23 12:46:10 +00:00CommentedSep 23, 2024 at 12:46 - 1\$\begingroup\$(\$n=1\$ may also be problematic)\$\endgroup\$Arnauld– Arnauld2024-09-23 13:00:56 +00:00CommentedSep 23, 2024 at 13:00
- \$\begingroup\$@Arnauld Eugh, give me a moment to fix my reference code.\$\endgroup\$Albert.Lang– Albert.Lang2024-09-23 13:08:19 +00:00CommentedSep 23, 2024 at 13:08
6 Answers6
JavaScript (ES6), 113 bytes
Expects(N)(n)(o) and returnsfalse forstrange,true forregular,2 forwrong.
N=>n=>g=(o,x,y=(U="toUpperCase")[+!x])=>~N--?g(o,n--?y:y=p=y[U](),x&&x+y):(s=x.substr(o,p.length))[U]()==p?s==p:2Method
We recursively build all patterns\$F_0\$ to\$F_N\$ using letters in lower case (ironically using the'o' and't' from the string'toUpperCase' for golfing reasons).
When\$F_n\$ is reached, we convert it to upper case, save it in\$p\$ and use the transformed string for the recursion. All subsequent regular occurrences of\$p\$ will therefore appear in upper case as well.
At the end of the process, we look for\$p\$ at offset\$o\$ in\$F_N\$:
- If\$p\$ appears in upper case, this is aregular match.
- If\$p\$ appears in lower or mixed case, this is astrange match.
- If\$p\$ doesn't appear at all, this is obviouslywrong.
Commented
N => n => // outer functions taking N and ng = ( // inner recursive function taking: o, // the offset o x, // a string x (initially undefined) y = ( // a string y initialized by using the U = "toUpperCase" // first 2 characters of "toUpperCase": )[+!x] // - "o" if x is undefined (1st iteration) // - "t" if x is defined (2nd iteration)) => //~N-- ? // if N != -1 (decrement afterwards): g( // do a recursive call: o, // pass o unchanged n-- ? // if n != 0 (decrement afterwards): y // use y unchanged : // else: y = p = y[U](), // update y and p to y in upper case x && x + y // if x is defined, pass x + y // (pass undefined otherwise) ) // end of recursive call: // else: ( // s = x.substr( // s = relevant slice of x o, p.length // at offset o and of size p.length ) // )[U]() == p ? // if s in upper case is equal to p: s == p // compare s with p (Boolean value) : // else: 2 // return 2 for 'wrong'05AB1E, 30bytes
1ÝλN¹›i‚»ë«]¹I‚è`IF¦¶Û}Dþ‚sδÅ?Inputs in the order\$n,N,o\$. Outputs[1,1] for regular;[0,1] for strange; and[0,0] for wrong.
Try it online orverify all test cases.
Explanation:
λ # Create a recursive environment, # to output the infinite sequence1Ý # Starting with a(0)=0 and a(1)=1 # Where every following a(x) is calculated as: # (implicitly push the previous terms a(x-2) and a(x-1)) N¹›i # If x is larger than first input `n`: ‚ # Pair the two previous terms: [a(x-2),a(x-1)] » # Join this pair with newline-delimiter ë # Else: « # Append term a(x-1) to a(x-2) instead ] # Close both the if-statement and recursive environment ¹I‚ # Push a pair of the first two inputs: [n,N] è # (0-based) index those into the infinite list` # Pop and push both separated to the stack IF # Loop the third input `o` amount of times: ¦ # Remove the first bit ¶Û # And also trim a potential leading newline character }D # After the loop: duplicate the off-set result þ # Remove all newlines from the copy, by only keeping digits ‚ # Pair the two together δ # Map over this pair: s Å? # Check for both whether it starts with the n'th term # (after which this pair is output implicitly as result)- \$\begingroup\$FYI, I've fixed my code for \$n<2\$ at the cost of +6 bytes.\$\endgroup\$Arnauld– Arnauld2024-09-23 13:14:11 +00:00CommentedSep 23, 2024 at 13:14
- 1\$\begingroup\$@Arnauld Thanks for the heads up, but with the way my recursive method is build up, a straight-forward fix seems to be 8 bytes (
¹„abD²èu©²ǝSλè«N²Qiu©]³.$Du‚®δÅ?). Can probably be golfed a bit to 6-7, but that would still tie it with my current approach, so I'll just leave it as is.\$\endgroup\$Kevin Cruijssen– Kevin Cruijssen2024-09-23 13:47:51 +00:00CommentedSep 23, 2024 at 13:47 - \$\begingroup\$Feels weird to see an
IFin 05AB1E...\$\endgroup\$Neil– Neil2024-09-23 14:29:19 +00:00CommentedSep 23, 2024 at 14:29
Charcoal, 75 bytes
NθNηNζFab⊞υιFθ⊞υ⭆²§υ⁻λ²I∧№⌕A§υθ§υηζ⊕∨‹η²№E⌕A§υ⊕⁻θηbΣE²×№…§υ⊕⁻θηι§υλL§υ⁻η¬λζTry it online! Link is to verbose version of code. Outputs0 for wrong,1 for strange and2 for regular. Explanation:
NθNηNζInputN,n ando.
Fab⊞υιFθ⊞υ⭆²§υ⁻λ²Generate the firstN+2 Fibonacci words.
I∧№⌕A§υθ§υηζIfo is not one of the places where thenth word appears in theNth word, then the output is0.
⊕∨‹η²Ifn<2 then the output is2.
№E⌕A§υ⊕⁻θηbΣE²×№…§υ⊕⁻θηι§υλL§υ⁻η¬λζOtherwise, theNth word is composed of copies of then-1th andnth words, given by theN-n+1th word, where ana in that word represents a copy of then-1th word and ab in that word represents a copy of thenth word. Using the lengths of then-1th andnth words, generate the offsets inside theNth word corresponding to all of thebs which represent all of the regularnth subwords, and check whethero is one of those.
JavaScript (Node.js), 99 bytes
(N,n,o)=>[0,n].map(k=>(F=n=>('ab'[n]||F(n-2)+F(n-1)).replace(/./,n-k&&'$&'))(N).indexOf(F(n),o)==o)Output[true,true] for regular,[true,false] for strange,[false,false] for wrong.
Let's denote\$x\#y\$ as string concatenation, and\$x\left[1:\right]\$ as string slicing which remove the first letter from\$x\$.
$$F(x)=\begin{cases}\textbf{a}&x=0\\\textbf{b}&x=1\\F(x-2)\#F(x-1)&x>1\end{cases}$$
$$G(x)=\begin{cases}F(x)&x\le 1\wedge x\ne n\\G(x-2)\#G(x-1)&x>1\wedge x\ne n\\\textbf{c}\#F(x)\left[1:\right]&x=n\end{cases}$$
- Define\$F(x)\$ as described in this question, Build\$F(N)\$ and\$F(n)\$ to verify if\$F(n)\$ matches\$F(N)\$ at position\$o\$.
- Define\$G(x)\$, it replace first letter of\$F(n)\$ by a third letter, while keeping other rules of\$F\$. Build\$G(N)\$ and\$G(n)\$ to verify if\$G(n)\$ matches\$G(N)\$ at position\$o\$.
- We collect above 2 values as an array and return it.
I have no idea how to prove its correctness, it simply passed all testcases. Any prove or bad cases are welcomed.
- 2\$\begingroup\$That's fairly similar to my reference implementation. I'd argue that it's correct in principle is kind of obvious, no?\$\endgroup\$Albert.Lang– Albert.Lang2024-09-24 07:14:59 +00:00CommentedSep 24, 2024 at 7:14
Python3, 356 bytes
I=lambda a:isinstance(a,str)T=lambda a:a if I(a)else''.join(T(b)for _,b in a)def O(t,n,C): if I(t):return for a,b in t: if a==n:yield C[0] if I(b):C[0]+=len(b) else:yield from O(b,n,C)def f(N,n,o): k=[(0,'a'),(t:=1,'b')] while(t:=t+1)<=N:k+=[(t,k[-2:])] K=dict(k) if o in[*O(K[N],n,[0])]:return 2 if T(K[N])[o:].startswith(T(K[n])):return 1Wolfram Language(Mathematica), 560 bytes
560 bytes, it can be golfed more.
Golfed version.Try it online!
S[a_]:=If[StringQ[a],a,StringJoin[S[Last[#]]&/@a]]P[a_,b_]:=Flatten[F[a,b,0]]F[a_,b_,c_]:=Module[{r={},d=c},If[StringQ[a],Return[{}]];Do[With[{i=e[[1]],v=e[[2]]},If[i==b,AppendTo[r,d]];If[StringQ[v],d+=StringLength[v],Module[{s=F[v,b,d]},r=Join[r,s];d+=StringLength[S[v]]]]],{e,a}];r]B[a_]:=Module[{b={{0,"a"},{1,"b"}},c=1},While[c+1<=a,c++;AppendTo[b,{c,Take[b,-2]}]];b]H[a_,b_,c_]:=Module[{d,e,f,g,h},d=B[a];e=Association[Rule@@@d];f=P[e[a],b];If[MemberQ[f,c],Return[2]];g=S[e[a]];h=S[e[b]];If[StringLength[g]>c&&StringStartsQ[StringDrop[g,c],h],1,Null]]Ungolfed version.Try it online!
(* Helper function to check if a value is a string *)isStringQ[value_] := StringQ[value](* Recursively flatten a nested structure to a string *)flattenToString[structure_] := If[isStringQ[structure], structure, StringJoin[flattenToString[Last[#]] & /@ structure] ](* Find all positions where targetIndex appears in the structure *)(* Returns a list of positions in the flattened string *)findPositions[structure_, targetIndex_] := Module[{}, Flatten[findPositionsHelper[structure, targetIndex, 0]] ]findPositionsHelper[structure_, targetIndex_, currentPos_] := Module[{results = {}, pos = currentPos}, If[isStringQ[structure], Return[{}] (* No positions found in a string *) ]; (* Process each element in the structure *) Do[ With[{index = element[[1]], content = element[[2]]}, (* If this element's index matches our target, record the current position *) If[index == targetIndex, AppendTo[results, pos] ]; (* Move position forward based on the content *) If[isStringQ[content], (* If content is a string, advance position by its length *) pos += StringLength[content], (* If content is a structure, recursively find positions and advance *) Module[{subResults = findPositionsHelper[content, targetIndex, pos]}, results = Join[results, subResults]; (* Advance position by the total length of the flattened subcontent *) pos += StringLength[flattenToString[content]]; ] ] ], {element, structure} ]; results ](* Build Fibonacci-like sequence structure *)buildFibonacciStructure[n_] := Module[{sequence, currentIndex}, sequence = {{0, "a"}, {1, "b"}}; currentIndex = 1; While[currentIndex + 1 <= n, currentIndex++; AppendTo[sequence, {currentIndex, Take[sequence, -2]}] ]; sequence ](* Main function to check Fibonacci pattern *)checkFibonacciPattern[N_, n_, offset_] := Module[{fibonacciSequence, fibonacciAssociation, positionsOfN, flattenedN, flattenedN2}, (* Build Fibonacci-like sequence structure *) fibonacciSequence = buildFibonacciStructure[N]; fibonacciAssociation = Association[Rule @@@ fibonacciSequence]; (* Check if offset matches any position where target index n appears in structure N *) positionsOfN = findPositions[fibonacciAssociation[N], n]; If[MemberQ[positionsOfN, offset], Return[2] (* regular pattern *) ]; (* Check if the flattened string from N starting at offset begins with flattened string of n *) flattenedN = flattenToString[fibonacciAssociation[N]]; flattenedN2 = flattenToString[fibonacciAssociation[n]]; If[StringLength[flattenedN] > offset && StringStartsQ[StringDrop[flattenedN, offset], flattenedN2], Return[1], (* strange pattern *) Return[Null] (* wrong pattern *) ] ](* Test data *)testCases = { {4, 0, 0, "regular"}, {4, 0, 1, "wrong"}, {4, 0, 2, "wrong"}, {4, 0, 3, "regular"}, {4, 0, 4, "wrong"}, {5, 0, 0, "wrong"}, {5, 0, 1, "regular"}, {5, 0, 2, "wrong"}, {5, 0, 3, "regular"}, {5, 0, 4, "wrong"}, {5, 0, 5, "wrong"}, {5, 0, 6, "regular"}, {5, 0, 7, "wrong"}, {4, 1, 0, "wrong"}, {4, 1, 1, "regular"}, {4, 1, 2, "regular"}, {4, 1, 3, "wrong"}, {4, 1, 4, "regular"}, {5, 1, 0, "regular"}, {5, 1, 1, "wrong"}, {5, 1, 2, "regular"}, {5, 1, 3, "wrong"}, {5, 1, 4, "regular"}, {5, 1, 5, "regular"}, {5, 1, 6, "wrong"}, {5, 1, 7, "regular"}, {1, 0, 0, "wrong"}, {10, 2, 7, "wrong"}, {7, 3, 0, "regular"}, {7, 3, 7, "strange"}, {7, 3, 9, "wrong"}, {7, 3, 10, "regular"}, {7, 3, 11, "wrong"}, {14, 7, 47, "strange"}, {14, 7, 479, "strange"}, {14, 7, 335, "strange"}, {14, 7, 335, "strange"}, {14, 7, 369, "strange"}, {14, 7, 513, "strange"}, {14, 7, 100, "wrong"}, {14, 7, 200, "wrong"}, {14, 7, 300, "wrong"}, {14, 7, 13, "regular"}, {14, 7, 123, "regular"}, {14, 7, 233, "regular"}, {14, 7, 322, "regular"}};(* Expected result mapping *)expectedResults = <| "regular" -> 2, "strange" -> 1, "wrong" -> Null|>;(* Run tests *)Print["\n=== RUNNING TESTS ==="];testResults = Table[ With[{N = testCase[[1]], n = testCase[[2]], offset = testCase[[3]], expected = testCase[[4]]}, Module[{result = checkFibonacciPattern[N, n, offset]}, { testCase, result, expectedResults[expected], result === expectedResults[expected] } ] ], {testCase, testCases}];(* Check if all tests passed *)allTestsPassed = AllTrue[testResults, Last];If[allTestsPassed, Print["All tests passed!"], Print["Some tests failed:"]; failedTests = Select[testResults, Not[Last[#]] &]; Print["Number of failed tests: ", Length[failedTests]]; Print["First few failed tests:"]; Take[failedTests, Min[5, Length[failedTests]]] // TableForm // Print];Explore related questions
See similar questions with these tags.



