Lucas Sequence in Ruby - ruby

The Lucas Sequence is a sequence of numbers. The first number of the sequence is 2. The second number of the Lucas Sequence is 1. To generate the next number of the sequence, we add up the previous two numbers. For example, the first six numbers of the sequence are: 2, 1, 3, 4, 7, 11, ...
Write a method lucasSequence that accepts a number representing a length as an arg. The method should return an array containing the Lucas Sequence up to the given length. Solve this recursively.
def lucas_sequence(length)
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
seq = lucas_sequence(length - 1)
next_el = seq[-1] + seq[-2]
seq << next_el
seq
end
p lucas_sequence(0) # => []
p lucas_sequence(1) # => [2]
p lucas_sequence(2) # => [2, 1]
p lucas_sequence(3) # => [2, 1, 3]
p lucas_sequence(6) # => [2, 1, 3, 4, 7, 11]
p lucas_sequence(8) # => [2, 1, 3, 4, 7, 11, 18, 29]
**I'm having a hard time understanding the recursion logic behind this. Can someone explain how the computer is solving this?
Does the computer read the length and then add up from [2,1] until it reaches its length? If so, how does it continuously count down? **

Recursion is the programming equivalent of mathematical induction. Given a series, assume that the problem is solved for the previous member of the series and provide the rule for generating this member.
So, consider just these lines:
def lucas_sequence(length)
seq = lucas_sequence(length - 1) # <1>
next_el = seq[-1] + seq[-2] # <2>
seq << next_el # <3>
seq # <4>
end
That says:
You want to know the lucas sequence of a certain length (length). Fine, first tell me the previous lucas sequence, the sequence that is one unit shorter than this (length-1). (That is the recursion: the lucas_sequence method, itself, calls the lucas_sequence method, but with a reduced length value.)
Add up the last two members of that shorter sequence...
...and append the sum to that shorter sequence...
...and the result is this sequence, the one you asked for.
And that's basically all there is to it! The only problem is that there is no place to start. We assume that for the seq of length 4, we have solved 3 already, which we get by assuming that we have solved 2 already, which we get by assuming we have solve 1 already... But we haven't actually solved any of those!
So we begin by backstopping the most degenerate cases:
return [] if length == 0
return [2] if length == 1
return [2, 1] if length == 2
Now the problem is solved if length is 0, 1, or 2, because we just give those answers directly. Okay, so if length is 3, we solve with reference to 2, which is known. Okay, if length is 4, we solve with reference to 3, and I just told you how to do that. Okay, if length is 5, we solve with reference to 4, and I just told you how to do that. And so on, for any length you care to give me.

So it is essentially a modified Fibonacci sequence. Best way to solve most structured sequences is with an Enumerator e.g.
lucas = Enumerator.new do |y|
a,b = 2,1
loop do
y << a
a, b = b, a + b
end
end
Then
lucas.first(10)
#=> [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
First we create a new Enumerator and then assign a and b to your starting values (2 and 1 respectively).
To generate the sequence we use a loop which will lazily yield the values to the yielder (y).
Here we push in a then we assign a to bs value and bs value to a + b in parallel to avoid overwriting a before the addition of a + b.

Related

Detect outlier in repeating sequence

I have a repeating sequence of say 0~9 (but may start and stop at any of these numbers). e.g.:
3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2
And it has outliers at random location, including 1st and last one, e.g.:
9,4,5,6,7,8,9,0,1,2,3,4,8,6,7,0,9,0,1,2,3,4,1,6,7,8,9,0,1,6
I need to find & correct the outliers, in the above example, I need correct the first "9" into "3", and "8" into "5", etc..
What I came up with is to construct a sequence with no outlier of desired length, but since I don't know which number the sequence starts with, I'd have to construct 10 sequences each starting from "0", "1", "2" ... "9". And then I can compare these 10 sequences with the given sequence and find the one sequence that match the given sequence the most. However this is very inefficient when the repeating pattern gets large (say if the repeating pattern is 0~99, I'd need to create 100 sequences to compare).
Assuming there won't be consecutive outliers, is there a way to find & correct these outliers efficiently?
edit: added some explanation and added the algorithm tag. Hopefully it is more appropriate now.
I'm going to propose a variation of #trincot's fine answer. Like that one, it doesn't care how many outliers there may be in a row, but unlike that one doesn't care either about how many in a row aren't outliers.
The base idea is just to let each sequence element "vote" on what the first sequence element "should be". Whichever gets the most votes wins. By construction, this maximizes the number of elements left unchanged: after the 1-liner loop ends, votes[i] is the number of elements left unchanged if i is picked as the starting point.
def correct(numbers, mod=None):
# this part copied from #trincot's program
if mod is None: # if argument is not provided:
# Make a guess what the range is of the values
mod = max(numbers) + 1
votes = [0] * mod
for i, x in enumerate(numbers):
# which initial number would make x correct?
votes[(x - i) % mod] += 1
winning_count = max(votes)
winning_numbers = [i for i, v in enumerate(votes)
if v == winning_count]
if len(winning_numbers) > 1:
raise ValueError("ambiguous!", winning_numbers)
winning_number = winning_numbers[0]
for i in range(len(numbers)):
numbers[i] = (winning_number + i) % mod
return numbers
Then, e.g.,
>>> correct([9,4,5,6,7,8,9,0,1,2,3,4,8,6,7,0,9,0,1,2,3,4,1,6,7,8,9,0,1,6])
[3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
but
>>> correct([1, 5, 3, 7, 5, 9])
...
ValueError: ('ambiguous!', [1, 4])
That is, it's impossible to guess whether you want [1, 2, 3, 4, 5, 6] or [4, 5, 6, 7, 8, 9]. They both have 3 numbers "right", and despite that there are never two adjacent outliers in either case.
I would do a first scan of the list to find the longest sublist in the input that maintains the right order. We will then assume that those values are all correct, and calculate backwards what the first value would have to be to produce those values in that sublist.
Here is how that would look in Python:
def correct(numbers, mod=None):
if mod is None: # if argument is not provided:
# Make a guess what the range is of the values
mod = max(numbers) + 1
# Find the longest slice in the list that maintains order
start = 0
longeststart = 0
longest = 1
expected = -1
for last in range(len(numbers)):
if numbers[last] != expected:
start = last
elif last - start >= longest:
longest = last - start + 1
longeststart = start
expected = (numbers[last] + 1) % mod
# Get from that longest slice what the starting value should be
val = (numbers[longeststart] - longeststart) % mod
# Repopulate the list starting from that value
for i in range(len(numbers)):
numbers[i] = val
val = (val + 1) % mod
# demo use
numbers = [9,4,5,6,7,8,9,0,1,2,3,4,8,6,7,0,9,0,1,2,3,4,1,6,7,8,9,0,1,6]
correct(numbers, 10) # for 0..9 provide 10 as argument, ...etc
print(numbers)
The advantage of this method is that it would even give a good result if there were errors with two consecutive values, provided that there are enough correct values in the list of course.
Still this runs in linear time.
Here is another way using groupby and count from Python's itertools module:
from itertools import count, groupby
def correct(lst):
groupped = [list(v) for _, v in groupby(lst, lambda a, b=count(): a - next(b))]
# Check if all groups are singletons
if all(len(k) == 1 for k in groupped):
raise ValueError('All groups are singletons!')
for k, v in zip(groupped, groupped[1:]):
if len(k) < 2:
out = v[0] - 1
if out >= 0:
yield out
else:
yield from k
else:
yield from k
# check last element of the groupped list
if len(v) < 2:
yield k[-1] + 1
else:
yield from v
lst = "9,4,5,6,7,8,9,0,1,2,3,4,8,6,7,0,9,0,1,2,3,4,1,6,7,8,9,0,1,6"
lst = [int(k) for k in lst.split(',')]
out = list(correct(lst))
print(out)
Output:
[3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
Edit:
For the case of [1, 5, 3, 7, 5, 9] this solution will return something not accurate, because i can't see which value you want to modify. This is why the best solution is to check & raise a ValueError if all groups are singletons.
Like this?
numbers = [9,4,5,6,7,8,9,0,1,2,3,4,8,6,7,0,9,0,1,2,3,4,1,6,7,8,9,0,1,6]
i = 0
for n in numbers[:-1]:
i += 1
if n > numbers[i] and n > 0:
numbers[i-1] = numbers[i]-1
elif n > numbers[i] and n == 0:
numbers[i - 1] = 9
n = numbers[-1]
if n > numbers[0] and n > 0:
numbers[-1] = numbers[0] - 1
elif n > numbers[0] and n == 0:
numbers[-1] = 9
print(numbers)

Getting the last x numbers from an infinite sequence in Ruby

This works in Ruby:
a = 4..Float::INFINITY
p a.take(4) #=> [4,5,6,7]
But I was wondering if it's possible to do something similar where the range would be from negative infinity up to a certain number, say 4, and have a method that will take the last, say 6 elements from that sequence, which would be [-1,0,1,2,3,4].
Getting the last N numbers from a range -infinity..4 is the same of selecting a range of 4..(4-N).
4.downto(4-5).to_a
# => [4, 3, 2, 1, 0, -1]
You can package it as a custom method
def lastn(from, n)
from.downto(from-n).to_a
end
How about the last method?
a.last(6)

How do I iterate through the digits of an integer? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Turning long fixed number to array Ruby
Well, I have to iterate over the digits of a integer in Ruby. Right now I was just splitting it up into an array, and then iterating over that. However I was wondering if there was a faster way to do this?
The shortest solution probably is:
1234.to_s.chars.map(&:to_i)
#=> [1, 2, 3, 4]
A more orthodox mathematical approach:
class Integer
def digits(base: 10)
quotient, remainder = divmod(base)
quotient == 0 ? [remainder] : [*quotient.digits(base: base), remainder]
end
end
0.digits #=> [0]
1234.digits #=> [1, 2, 3, 4]
0x3f.digits(base: 16) #=> [3, 15]
You can use the old trick of modulus/divide by 10, but this won't be measurably faster unless you have huge numbers, and it will give the digits to you backwards:
i = 12345
while i > 0
digit = i % 10
i /= 10
puts digit
end
Output:
5
4
3
2
1
split=->(x, y=[]) {x < 10 ? y.unshift(x) : split.(x/10, y.unshift(x%10))}
split.(1000) #=> [1,0,0,0]
split.(1234) #=> [1,2,3,4]
Ruby has divmod, which will calculate both x%10and x/10 in one go:
class Integer
def split_digits
return [0] if zero?
res = []
quotient = self.abs #take care of negative integers
until quotient.zero? do
quotient, modulus = quotient.divmod(10) #one go!
res.unshift(modulus) #put the new value on the first place, shifting all other values
end
res # done
end
end
p 135.split_digits #=>[1, 3, 5]
For things like Project Euler, where speed is of some importance, this is nice to have. Defining it on Integer causes it to be available on Bignum too.
I like to use enumerators for this purpose:
class Integer
def digits
to_s.each_char.lazy.map(&:to_i)
end
end
This gives you access to all the good Enumerator stuff:
num = 1234567890
# use each to iterate over the digits
num.digits.each do |digit|
p digit
end
# make them into an array
p num.digits.to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# or take only some digits
p num.digits.take(5) # => [1, 2, 3, 4, 5]
# ...
Try mod by 10 (will give you the last digit), then divide by 10 (will give you the rest of digits), repeat this until you're down to the final digit. Of course, you'll have to reverse the order if you want to go through the digits from left to right.

Generating unique sorted partitions in Ruby

I'm trying to generate the set of sequences as shown below, not in any particularly order, but here its shown as a descending sequence. Note that each sequence also descends as I'm interested in combinations, not permutations. I'd like to store each sequence as an array..or the set of sequences as an array of arrays more preferably, but first things first.
6
5 1
4 2
4 1 1
3 3
3 2 1
3 1 1 1
2 2 2
2 2 1 1
2 1 1 1 1
1 1 1 1 1 1
Right now I am simply focusing on generating these sets and I'm trying to do it recursively. Essentially..these are all the sequences of numbers when combines will give some total..in this case 6. But notice how when the first number is 3, the set of numbers which follows is simply the set of sequences that gives a total of 3. In other words 6(target total) - 3 (first number) = 3 (set of sequences that give total of 3). Thus, should be able to do this recursively.
I tried to code as follows (and yes, this is my first language and yes, I've only been studying for about a week so I'm sure its all screwed up) but so far no luck. I think if I can just get the very core of the recursion working and put the values of all the objects to the screen so I can trace it line by line, I think I can move ahead, but between the logic and the syntax, I'm at a stand still.
My logic is:
define a method that passes 'count' representing the total being targeted.
create an array which is going to hold a given sequence of values
create an index which represents the position in the array (ignoring zero position).
define 'delta' and initialize it to the value of 'count' and have it represent the remaining target sum of the rest of the array. (since there is nothing in the array initially, the delta is the same as the count.)
Then, cycle through the possibilities for the next(first) value of the sequence starting from 1 and ending, obviously, with the maximum possible, which is the value of 'count' itself. Determine the new delta for each value in the cycle.
If the delta is 0, you are done otherwise determine this new sequence which will give this new delta. Likely need to append the new sequence to the current sequence as well.
i=0
def seq(count)
cvc=Array.new # array to hold the number values
i=i+1 # position index for the array
puts 'i is ' + i.to_s
delta=count
puts ' delta is ' + delta.to_s
for value in 1..delta do # value represents the number value
cvc[i]=value
puts 'cvc[i] is ' + cvc[i].to_s
delta = delta-cvc.sum
puts 'new delta is '+ delta.to_s
if delta >1 then count=delta
seq(count)
end
end
end
Here's a solution:
def expand(n, max = n)
return [[]] if n == 0
[max, n].min.downto(1).flat_map do |i|
expand(n-i, i).map{|rest| [i, *rest]}
end
end
expand(6) # => [[6], [5, 1], [4, 2], [4, 1, 1], [3, 3], [3, 2, 1], [3, 1, 1, 1], [2, 2, 2], [2, 2, 1, 1], [2, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]

Loop through different sets of unique permutations

I'm having a hard time getting started to layout code for this problem.
I have a fixed amount of random numbers, in this case 8 numbers.
R[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
That are going to be placed in 3 sets of numbers, with the only constraint that each set contain minimum one value, and each value can only be used once. Edit: all 8 numbers should be used
For example:
R1[] = { 1, 4 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
I need to loop through all possible combinations of a set R1, R2, R3. Order is not important, so if the above example happened, I don't need
R1[] = { 4, 1 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
NOR
R1[] = { 2, 8, 5, 6 }
R2[] = { 7, 3 }
R3[] = { 1, 4 }
What is a good method?
I have in front of me Knuth Volume 4, Fascicle 3, Generating all Combinations and Partitions, section 7.2.1.5 Generating all set partitions (page 61 in fascicle).
First he details Algorithm H, Restricted growth strings in lexicographic order due to George Hutchinson. It looks simple, but I'm not going to dive into it just now.
On the next page under an elaboration Gray codes for set partitions he ponders:
Suppose, however, that we aren't interested in all of the partitions; we might want only the ones that have m blocks. Can we run this through the smaller collection of restricted growth strings, still changing one digit at a time?
Then he details a solution due to Frank Ruskey.
The simple solution (and certain to be correct) is to code Algorithm H filtering on partitions where m==3 and none of the partitions are the empty set (according to your stated constraints). I suspect Algorithm H runs blazingly fast, so the filtering cost will not be large.
If you're implementing this on an 8051, you might start with the Ruskey algorithm and then only filter on partitions containing the empty set.
If you're implementing this on something smaller than an 8051 and milliseconds matter, you can seed each of the three partitions with a unique element (a simple nested loop of three levels), and then augment by partitioning on the remaining five elements for m==3 using the Ruskey algorithm. You won't have to filter anything, but you do have to keep track of which five elements remain to partition.
The nice thing about filtering down from the general algorithm is that you don't have to verify any cleverness of your own, and you change your mind later about your constraints without having to revise your cleverness.
I might even work a solution later, but that's all for now.
P.S. for the Java guppies: I discovered searching on "George Hutchison restricted growth strings" a certain package ca.ubc.cs.kisynski.bell with documentation for method growthStrings() which implements the Hutchison algorithm.
Appears to be available at http://www.cs.ubc.ca/~kisynski/code/bell/
Probably not the best approach but it should work.
Determine number of combinations of three numbers which sum to 8:
1,1,6
1,2,5
1,3,4
2,2,4
2,3,3
To find the above I started with:
6,1,1 then subtracted 1 from six and added it to the next column...
5,2,1 then subtracted 1 from second column and added to next column...
5,1,2 then started again at first column...
4,2,2 carry again from second to third
4,1,3 again from first...
3,2,3 second -> third
3,1,4
knowing that less than half is 2 all combinations must have been found... but since the list isn't long we might as well go to the end.
Now sort each list of 3 from greatest to least(or vice versa)
Now sort each list of 3 relative to each other.
Copy each unique list into a list of unique lists.
We now have all the combinations which add to 8 (five lists I think).
Now consider a list in the above set
6,1,1 all the possible combinations are found by:
8 pick 6, (since we picked six there is only 2 left to pick from) 2 pick 1, 1 pick 1
which works out to 28*2*1 = 56, it is worth knowing how many possibilities there are so you can test.
n choose r (pick r elements from n total options)
n C r = n! / [(n-r)! r!]
So now you have the total number of iterations for each component of the list for the first one it is 28...
Well picking 6 items from 8 is the same as creating a list of 8 minus 2 elements, but which two elements?
Well if we remove 1,2 that leaves us with 3,4,5,6,7,8. Lets consider all groups of 2... Starting with 1,2 the next would be 1,3... so the following is read column by column.
12
13 23
14 24 34
15 25 35 45
16 26 36 46 56
17 27 37 47 57 67
18 28 38 48 58 68 78
Summing each of the above columns gives us 28. (so this only covered the first digit in the list (6,1,1) repeat the procedure for the second digit (a one) which is "2 Choose 1" So of the left over two digits from the above list we pick one of two and then for the last we pick the remaining one.
I know this is not a detailed algorithm but I hope you'll be able to get started.
Turn the problem on it's head and you'll find a straight-forward solution. You've got 8 numbers that each need to be assigned to exactly one group; The "solution" is only a solution if at least one number got assigned to each group.
The trivial implementation would involve 8 for loops and a few IF's (pseudocode):
for num1 in [1,2,3]
for num2 in [1,2,3]
for num3 in [1,2,3]
...
if ((num1==1) or (num2==1) or (num3 == 1) ... (num8 == 1)) and ((num1 == 2) or ... or (num8 == 2)) and ((num1 == 3) or ... or (num8 == 3))
Print Solution!
It may also be implemented recursively, using two arrays and a couple of functions. Much nicer and easier to debug/follow (pseudocode):
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
positions = [0, 0, 0, 0, 0, 0, 0, 0]
function HandleNumber(i) {
for position in [1,2,3] {
positions[i] = position;
if (i == LastPosition) {
// Check if valid solution (it's valid if we got numbers in all groups)
// and print solution!
}
else HandleNumber(i+1)
}
}
The third implementation would use no recursion and a little bit of backtracking. Pseudocode, again:
numbers = [1,2,3,4,5,6,7,8]
groups = [0,0,0,0,0,0,0,0]
c_pos = 0 // Current position in Numbers array; We're done when we reach -1
while (cpos != -1) {
if (groups[c_pos] == 3) {
// Back-track
groups[c_pos]=0;
c_pos=c_pos-1
}
else {
// Try the next group
groups[c_pos] = groups[c_pos] + 1
// Advance to next position OR print solution
if (c_pos == LastPostion) {
// Check for valid solution (all groups are used) and print solution!
}
else
c_pos = c_pos + 1
}
}
Generate all combinations of subsets recursively in the classic way. When you reach the point where the number of remaining elements equals the number of empty subsets, then restrict yourself to the empty subsets only.
Here's a Python implementation:
def combinations(source, n):
def combinations_helper(source, subsets, p=0, nonempty=0):
if p == len(source):
yield subsets[:]
elif len(source) - p == len(subsets) - nonempty:
empty = [subset for subset in subsets if not subset]
for subset in empty:
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+1):
yield combination
subset.pop()
else:
for subset in subsets:
newfilled = not subset
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+newfilled):
yield combination
subset.pop()
assert len(source) >= n, "Not enough items"
subsets = [[] for _ in xrange(n)]
for combination in combinations_helper(source, subsets):
yield combination
And a test:
>>> for combination in combinations(range(1, 5), 2):
... print ', '.join(map(str, combination))
...
[1, 2, 3], [4]
[1, 2, 4], [3]
[1, 2], [3, 4]
[1, 3, 4], [2]
[1, 3], [2, 4]
[1, 4], [2, 3]
[1], [2, 3, 4]
[2, 3, 4], [1]
[2, 3], [1, 4]
[2, 4], [1, 3]
[2], [1, 3, 4]
[3, 4], [1, 2]
[3], [1, 2, 4]
[4], [1, 2, 3]
>>> len(list(combinations(range(1, 9), 3)))
5796

Resources