Created
October 26, 2010 16:40
-
-
Save etiennebarrie/647255 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'rubygems' | |
| require 'rspec' | |
| class Sequence | |
| def initialize(*elements) | |
| @elements = elements.sort | |
| end | |
| def elements | |
| @elements.dup | |
| end | |
| def sum | |
| @elements.inject(:+) | |
| end | |
| def has_digit_trio? | |
| counts.any? { |value| value > 2 } | |
| end | |
| def has_digit_pair? | |
| counts.any? { |count| count == 2 } | |
| end | |
| def valid? | |
| return false if has_digit_trio? | |
| return false unless has_digit_pair? | |
| sum == 15 | |
| end | |
| def next | |
| elements = self.elements | |
| return Sequence.new(1) if elements.empty? | |
| new_element = elements.last | |
| new_elements = elements | |
| sequence = nil | |
| loop do | |
| sequence = Sequence.new(*new_elements + [new_element]) | |
| if sequence.has_digit_trio? | |
| elsif sequence.sum > 15 | |
| new_element = new_elements.pop # throw last away | |
| else | |
| break | |
| end | |
| new_element = new_elements.pop while new_element == 9 | |
| return nil unless new_element # empty set, search over | |
| new_element += 1 | |
| end | |
| sequence | |
| end | |
| def next_valid | |
| sequence = self | |
| begin | |
| sequence = sequence.next | |
| end while sequence && !sequence.valid? | |
| sequence | |
| end | |
| private | |
| def counts | |
| histogram = Hash.new(0) | |
| @elements.each do |element| | |
| histogram[element] += 1 | |
| end | |
| histogram.values | |
| end | |
| def backtrack(elements) | |
| new_elements = elements | |
| new_element = new_elements.pop while new_elements.last == 9 | |
| if new_element | |
| new_elements << new_element + 1 | |
| end | |
| end | |
| end | |
| describe Sequence do | |
| it "should sum its elements" do | |
| sequence = Sequence.new(1, 2, 3, 4) | |
| sequence.sum.should == 10 | |
| end | |
| it "should return its elements" do | |
| sequence = Sequence.new(1, 2) | |
| elements = sequence.elements | |
| elements.should include(1) | |
| elements.should include(2) | |
| elements.should_not include(3) | |
| end | |
| describe "#valid?" do | |
| it "should return false when there is one element more than two times" do | |
| sequence = Sequence.new(1, 1, 1) | |
| sequence.should have_digit_trio | |
| sequence.should_not be_valid | |
| end | |
| it "should return true when there is more than one element two times" do | |
| sequence = Sequence.new(1, 1, 2, 2) | |
| sequence.should have_digit_pair | |
| sequence.should_not be_valid | |
| end | |
| it "should return false when there is no element two times" do | |
| sequence = Sequence.new(1, 2, 3) | |
| sequence.should_not have_digit_pair | |
| sequence.should_not be_valid | |
| end | |
| it "should return false when the sum is not 15" do | |
| sequence = Sequence.new(1, 2, 3, 3, 5) | |
| sequence.should_not be_valid | |
| end | |
| it "should return true for known valid sequences" do | |
| Sequence.new(1, 1, 6, 7).should be_valid | |
| Sequence.new(1, 1, 2, 3, 8).should be_valid | |
| Sequence.new(1, 1, 2, 3, 4, 4).should be_valid | |
| end | |
| end | |
| describe "#next" do | |
| it "should start with a simple sequence" do | |
| sequence = Sequence.new.next | |
| sequence.sum.should == 1 | |
| sequence.elements.should include(1) | |
| end | |
| it "should make a new sequence" do | |
| [[1], [1,9,9]].each do |elements| | |
| Sequence.new(*elements).next.elements.should_not == elements | |
| end | |
| end | |
| it "should not make a sequence with a digit trio" do | |
| sequence = Sequence.new(1, 1).next | |
| sequence.should_not have_digit_trio | |
| end | |
| it "should not make a sequence with a number larger than 9" do | |
| sequence = Sequence.new(1, 9, 9).next | |
| sequence.elements.should_not include(10) | |
| end | |
| it "should not make a sequence with a sum larger than 15" do | |
| sequence = Sequence.new(1, 9).next | |
| sequence.sum.should_not > 15 | |
| end | |
| it "should stop when there's no other choice" do | |
| sequence = Sequence.new(9, 9) | |
| sequence.next.should be_nil | |
| end | |
| end | |
| describe "#next_valid" do | |
| it "should return nil or a valid sequence" do | |
| sequence = Sequence.new | |
| while sequence = sequence.next_valid | |
| sequence.should be_valid | |
| end | |
| sequence.should be_nil | |
| end | |
| end | |
| end | |
| if __FILE__ == $0 | |
| puts <<-EOS.each_line.map(&:strip) | |
| All unique sequences of digits such as: | |
| * Each element of the sequence is a digit between 1 and 9 | |
| * The digits add to 15 | |
| * There is at least 1 digit that appears exactly twice | |
| * No digit appears more than twice | |
| * Order is irrelevant [1, 1, 2, 3, 8] and [1, 3, 2, 1, 8] are the same sequence and only count once. | |
| EOS | |
| sequence = Sequence.new | |
| sequences = [] | |
| while sequence = sequence.next_valid | |
| puts sequence.elements.join(', ') | |
| sequences << sequence | |
| end | |
| puts "#{sequences.size} sequences found" | |
| puts | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment