I'm having a lot of issues writing a Quicksort algorithm in Ruby. I'm coming from C++/ Java, so my code could be completely wrong:
def quicksort(*list)
if list.empty?
return list
end
$pivot = list.sample
list.delete_at(list.index($pivot))
current_element = list[0]
$smaller = Array.new
$larger = Array.new
list.each do |x|
if (list[x] <= $pivot)
$smaller << list[x]
else
$larger << list[x]
end
end
$sorted = Array.new
$sorted << self.quicksort(*$smaller)
$sorted << pivot
$sorted << self.quicksort(*$larger)
$sorted.flatten!
return *$sorted
end
myArray = [5, 4, 3, 2, 1]
sorted = Array.new(quicksort(myArray))
myArray.each do |x|
print x
end
print "\n"
This is the error that I'm getting:
My error
Can you not set an array equal to a function that returns an array, like you could in C++ or Java?
Editing your code you must have a class to define methods in IRB. In addition I fixed the bug with missing dollar before pivot.
class SortingAlgorithms
def self.quicksort(*list)
if list.empty?
return list
end
$pivot = list.sample
list.delete_at(list.index($pivot))
current_element = list[0]
$smaller = Array.new
$larger = Array.new
list.each do |x|
if (list[x] <= $pivot)
$smaller << list[x]
else
$larger << list[x]
end
end
$sorted = Array.new
$sorted << self.quicksort(*$smaller)
$sorted << $pivot
$sorted << self.quicksort(*$larger)
$sorted.flatten!
return *$sorted
end
end
myArray = [5, 4, 3, 2, 1]
sorted = Array.new(SortingAlgorithms.quicksort(myArray))
myArray.each do |x|
print x
end
print "\n"
But, beautiful ruby way provided in another thread.
Related
I’m currently doing Knight’s Travails project.
In this project you need to find the shortest way from A to B for the chess knight.
I don’t know why my program crashes when it comes to breadth-first search function. I cannot catch it with debugger because VScode freezes at reading variable “root” inside knight_moves.
Could you help me find the ussue?
I’ve created the board. It has links to every cell of the board according position of the cell.
I’ve created links between cells with add_edges function. Links are possible ways to move.
So far I’ve got the code below
class Node
attr_reader :pos
attr_accessor :children, :search_info
def initialize (row, column)
#pos = [row, column]
#children = nil
#search_info = Hash.new
end
end
class Board
attr_reader :show
def initialize
create_board
end
def create_board
board = []
8.times do |x|
board<<[x]
end
board.each_with_index do |item, index|
8.times do |x|
board[index] << x unless x == index
end
end
board.each do |x|
x.sort!
end
#board = board
end
def show
#board
end
def fill_with_nodes
#board.each_with_index do |item, index|
item.map! {|column| Node.new(index,column)}
end
end
def add_edges
#board.each_with_index do |row, index|
row.each do |node|
node.children = []
node.children = node.children << #board[node.pos[0]-2][node.pos[1]-1] if (0..7).include?(node.pos[0]-2) && (0..7).include?(node.pos[1]-1)
node.children = node.children << #board[node.pos[0]-2][node.pos[1]+1] if (0..7).include?(node.pos[0]-2) && (0..7).include?(node.pos[1]+1)
node.children = node.children << #board[node.pos[0]+2][node.pos[1]-1] if (0..7).include?(node.pos[0]+2) && (0..7).include?(node.pos[1]-1)
node.children = node.children << #board[node.pos[0]+2][node.pos[1]+1] if (0..7).include?(node.pos[0]+2) && (0..7).include?(node.pos[1]+1)
node.children = node.children << #board[node.pos[0]-1][node.pos[1]-2] if (0..7).include?(node.pos[0]-1) && (0..7).include?(node.pos[1]-2)
node.children = node.children << #board[node.pos[0]+1][node.pos[1]-2] if (0..7).include?(node.pos[0]+1) && (0..7).include?(node.pos[1]-2)
node.children = node.children << #board[node.pos[0]-1][node.pos[1]+2] if (0..7).include?(node.pos[0]-1) && (0..7).include?(node.pos[1]+2)
node.children = node.children << #board[node.pos[0]+1][node.pos[1]+2] if (0..7).include?(node.pos[0]+1) && (0..7).include?(node.pos[1]+2)
end
end
end
def cell (row, column)
#board[row][column]
end
def knight_moves (start, finish)
raise StandardError.new("Invalid start") unless (0..7).include?(start[0]) || (0..7).include?(start[1])
raise StandardError.new("Invalid finish") unless (0..7).include?(finish[0]) || (0..7).include?(finish[1])
queue = []
root = #board[finish[0]][finish[1]]
root.search_info[:distanse] = 0
queue << root
until queue.empty?
node = queue.shift
break if node.pos == [start[0],start[1]]
node.children.each do |child|
unless child.search_info[:distanse]
child.search_info[:distanse] = node.search_info[:distanse] + 1
child.search_info[:predecessor] = node
queue << child
end
end
end
end
end
#This part is for testing
puts a = Board.new
puts a.show.to_s
a.fill_with_nodes
puts a.show.to_s
a.add_edges
a.knight_moves([0,0], [0,1])
def show_cell(board,row, column)
puts ""
puts board.cell(row,column).pos.to_s, board.cell(row,column).children.map {|child| child.pos}.to_s ,board.cell(row,column).search_info.to_s
end
show_cell(a,2,2)
Edit: I've found that line "child.search_info[:predecessor] = node" crashes the programm. And if I use #variable to store "predecessor" instead of hash the programm runs. I don't know why though. What's the reason?
As for me, the main issue with the code is its unnecessary ("incidental") complexity.
Yes, the task you're solving can be reduced to a graph traversal problem, but it doesn't mean you must represent the graph explicitly. For this particular task - where all the possible moves from the arbitrary cell are well-defined and the board itself is limited - you can easily calculate the graph edges on the fly (and without all this additional machinery that makes your code so hard to reason about - even for you). Explicit representation of the board looks redundant too (again, for this particular task).
Taking all this into account, the solution might be as simple as:
class Knight
def initialize
#knight_moves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]
end
def move(start, stop)
visited = {}
queue = [[stop, nil]]
while queue.any?
current_cell, next_cell = queue.shift
next if visited.has_key?(current_cell)
visited[current_cell] = next_cell
return build_path(start, stop, visited) if current_cell == start
possible_moves(current_cell).each do |next_move|
queue << [next_move, current_cell] unless visited.has_key?(next_move)
end
end
end
private
def possible_moves(cell)
#knight_moves.
map { |x, y| [cell.first + x, cell.last + y] }.
select(&method(:valid_move?))
end
def build_path(start, stop, visited)
path = [start]
while next_cell = visited[path.last]
path << next_cell
end
path.last == stop ? path : nil
end
def valid_move?(cell)
cell.all? { |n| n >= 0 && n <= 7 }
end
end
knight = Knight.new
knight.move [0,0], [0,1] #=> [[0, 0], [2, 1], [1, 3], [0, 1]]
So I want to merge overlapping ranges and it should like the following:
Input: ranges = [(1..2), (3..6), (5..8)]
Output: expected = [(1..2), (3..8)]
but when the code iterate over the intervals and goes to the else statement I just get a message "function_merge.rb:9:in block in merge': undefined methodend=' for 2..19:Range (NoMethodError)"
I tried to save merged.last.end and interval.end to variables, rewrote the if statement over couple of lines (if interval.end > merged.last.end merged.last.end = interval.end end) but all that didn't work :-(
def merge(intervals)
merged = []
intervals.sort_by! { |interval| interval.begin }
intervals.each do |interval|
if merged.empty? || merged.last.end < interval.begin
merged << interval
else
merged.last.end = interval.end if interval.end > merged.last.end
end
end
return merged
end
I don't understand why I get this error message since "end" is a range method? I just want to "update" the merged.last.end with the interval.end number.
If you have any tips how to solve it, would be very nice :-)
As Sebastian points out, Ranges are immutable. Instead of trying to change the Range you'll have to make a new one.
def merge(intervals)
merged = []
intervals.sort_by! { |interval| interval.begin }
intervals.each do |interval|
if merged.empty? || merged.last.end < interval.begin
merged << interval
else
merged[-1] = Range.new(merged.last.begin, interval.end, interval.exclude_end?)
end
end
return merged
end
It has been explained that ranges are immutable. The question implies the elements covered by the ranges are all comparable (e.g, not ['a'..'z', 1..10]). I assume that the array of ranges does not contain a mix of finite and infinite ranges.
Solution
Code
def distill(arr)
a = arr.reject { |r| r.exclude_end? ? (r.end <= r.begin) : r.end < r.begin }.
sort_by(&:begin)
return [] if a.empty?
combined = []
curr = a.shift
loop do
break (combined << curr) if a.empty?
nxt = a.shift
if nxt.begin > curr.end
combined << curr
curr = nxt
else
last = [curr, nxt].max_by { |r| [r.end, r.exclude_end? ? 0 : 1] }
curr = last.exclude_end? ? (curr.begin...last.end) :
curr.begin..last.end
end
end
end
Examples
distill [5..8, 7...9, 9..11, 1...4, 38..37]
#=> [1...4, 5..11]
distill [1.5...2.2, 2.2..3.0, 3.0...4.5, 4.7..5.3, 5.2..4.6]
#=> [1.5...4.5, 4.7..5.3]
distill ['a'..'d', 'c'..'f', 'b'..'g']
# 'a'..'g'
Explanation
See Range#exclude_end?.
The steps for the first example are as follows.
arr = [5..8, 7...9, 9..11, 1...4, 38..37]
a = arr.reject { |r| r.exclude_end? ? (r.end <= r.begin) : r.end < r.begin }.
sort_by(&:begin)
#=> [1...4, 5..8, 7...9, 9..11]
a.empty?
#=> false, so do not return
combined = []
curr = a.shift
#=> 1...4
a #=> [5..8, 7...9, 9..11]
The calculations within the loop can be best explained by salting the code with puts statements and displaying the results.
loop do
puts "a.empty? #=> true, so break #{combined + [curr]}" if a.empty?
break (combined << curr) if a.empty?
puts "a.empty? #=> false"
nxt = a.shift
puts "nxt=#{nxt}, a=#{a}"
puts "nxt.begin=#{nxt.begin} > #{curr.end} = curr.end = #{nxt.begin > curr.end}"
if nxt.begin > curr.end
combined << curr
puts "combined << #{curr} = #{combined}"
curr = nxt
puts "curr = nxt = #{curr}"
else
last = [curr, nxt].max_by { |r| [r.end, r.exclude_end? ? 0 : 1] }
puts "last=#{last}, last.exclude_end?=#{last.exclude_end?}"
curr = last.exclude_end? ? (curr.begin...last.end) :
curr.begin..last.end
puts "new value of curr=#{curr}"
end
puts
end
a.empty? #=> false
nxt=5..8, a=[7...9, 9..11]
nxt.begin=5 > 4 = curr.end = true
combined << 1...4 = [1...4]
curr = nxt = 5..8
a.empty? #=> false
nxt=7...9, a=[9..11]
nxt.begin=7 > 8 = curr.end = false
last=7...9, last.exclude_end?=true
new value of curr=5...9
a.empty? #=> false
nxt=9..11, a=[]
nxt.begin=9 > 9 = curr.end = false
last=9..11, last.exclude_end?=false
new value of curr=5..11
a.empty? #=> true, so break [1...4, 5..11]
It is sometimes convenient to be able to return an empty (but valid) range such as 38..37; one should not think of empty ranges as necessarily being an indication that something is amiss.
Alternative solution
If the ranges are all finite, as in the example, and the combined sizes of the ranges is not excessive, one could write the following.
Code
def distill(arr)
arr.flat_map(&:to_a).
uniq.
sort.
chunk_while { |x,y| y == x.next }.
map { |a| a.first..a.last }
end
Examples
distill [5..8, 7...9, 9..11, 1...4, 38..37]
#=> [1..3, 5..11]
distill ['a'..'d', 'c'..'f', 'b'..'g']
# 'a'..'g'
Explanation
The steps for the first example are as follows.
arr = [5..8, 7...9, 9..11, 1...4, 38..37]
a = arr.flat_map(&:to_a)
#=> => [5, 6, 7, 8, 7, 8, 9, 10, 11, 1, 2, 3]
b = a.uniq
#=> [5, 6, 7, 8, 9, 10, 11, 1, 2, 3]
c = b.sort
#=> [1, 2, 3, 5, 6, 7, 8, 9, 10, 11]
d = c.chunk_while { |x,y| y == x.next }
#=> #<Enumerator: #<Enumerator::Generator:0x00005c2683af8dd0>:each>
e = d.map { |a| a.first..a.last }
#=> [1..3, 5..11]
One can convert the enumerator d to an array to see the elements it will generate and pass to chunk_while's block:
d.to_a
#=> [[1, 2, 3], [5, 6, 7, 8, 9, 10, 11]]
See Enumerable#chunk_while. One could alternatively use Enumerable#slice_when.
def encrypt(string)
alphabet = ("a".."b").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
I'm trying to get "abc" to print out "bcd" and "xyz" to print "yza". I want to advance the letter forward by 1. Can someone point me to the right direction?
All I had to do was change your alphabet array to go from a to z, not a to b, and it works fine.
def encrypt(string)
alphabet = ("a".."z").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
Another way to solve the issue, that I think is simpler, personally, is to use String#tr:
ALPHA = ('a'..'z').to_a.join #=> "abcdefghijklmnopqrstuvwxyz"
BMQIB = ('a'..'z').to_a.rotate(1).join #=> "bcdefghijklmnopqrstuvwxyza"
def encrypt(str)
str.tr(ALPHA,BMQIB)
end
def decrypt(str)
str.tr(BMQIB,ALPHA)
end
encrypt('pizza') #=> "qjaab"
decrypt('qjaab') #=> "pizza"
Alternatively if you don't want to take up that memory storing the alphabet you could use character codings and then just use arithmetic operations on them to shift the letters:
def encrypt(string)
result = ""
idx = 0
while idx < string.length
result += (string[idx].ord == 32 ? (string[idx].chr) : (string[idx].ord+1).chr)
idx += 1
end
result
end
Other strange thing about ruby is that you do not need to explicitly return something at the end of the method body. It just returns the last thing by default. This is considered good style amongst ruby folks.
Your question has been answered, so here are a couple of more Ruby-like ways of doing that.
Use String#gsub with a hash
CODE_MAP = ('a'..'z').each_with_object({}) { |c,h| h[c] = c < 'z' ? c.next : 'a' }
#=> {"a"=>"b", "b"=>"c",..., "y"=>"z", "z"=>"a"}
DECODE_MAP = CODE_MAP.invert
#=> {"b"=>"a", "c"=>"b",..., "z"=>"y", "a"=>"z"}
def encrypt(word)
word.gsub(/./, CODE_MAP)
end
def decrypt(word)
word.gsub(/./, DECODE_MAP)
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
Use String#gsub with Array#rotate
LETTERS = ('a'..'z').to_a
#=> ["a", "b", ..., "z"]
def encrypt(word)
word.gsub(/./) { |c| LETTERS.rotate[LETTERS.index(c)] }
end
def decrypt(word)
word.gsub(/./) { |c| LETTERS.rotate(-1)[LETTERS.index(c)] }
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
Here is a brain bender.
I am trying to rewrite the Ruby Inject method. I have got as far as below.
class Array
def injector(input = nil)
if input == nil
num = self.first
else
num = input
end
self[0..-1].each do |x|
num = yield(num, x)
end
return num
end
end
It is passing some tests, but it is not fully accurate, for example;
[1,2,3,4,5].injector(0) {|x,y| x + y} #=> 14
As opposed to the expected output 15, is it a rounding error? I cannot seem to figure this one out
Additional example (above updated [0..-1]):
[9,8,7,6,5].injector {|x,y| x * y} #=> 136080
Ruby .inject outputs 15120
The starting index is important as it depends on your input.
class Array
def injector(input = nil)
if input.nil?
start = 1
num = self.first
else
start = 0
num = input
end
self[start..-1].each do |x|
num = yield(num, x)
end
return num
end
end
Using nil as the default is probably wrong, I should be able to pass nil in as the default memo.
class Array
def injector(memo = (i=1; first))
(i||0).upto(length-1) { |i| memo = yield memo, self[i] }
memo
end
end
[1,2,3,4,5].injector(1) { |sum, n| sum + n }
[1,2,3,4,5].injector(0) { |sum, n| sum + n }
[1,2,3,4,5].injector { |sum, n| sum + n }
[1,2,3].injector(2) { |product, n| product * n }
[1,2,3].injector(1) { |product, n| product * n }
[1,2,3].injector { |product, n| product * n }
['b', 'c', 'd'].injector('a') { |str, char| str + char } # => "abcd"
['b', 'c', 'd'].injector { |str, char| str + char } # => "bcd"
seen = []
[1].injector(nil) { |prev, crnt| seen << prev << crnt }
seen # => [nil, 1]
Details of the problem: To find if any combination of the array adds to the largest number found in the array.
Here are the steps I am trying to implement:
Extract the largest number from the array
Create a new array of
all the potential combinations that could be added by using
.combination
Test to see if any of these combinations equals the largest number in the original array.
Status: So far, I am just receiving an unexpected end error for the last end in the code. (I have found different answers online on how to solve the subset sums problem in Ruby, but would like to figure out how to solve it using the logic I have used so far.)
Any help would be great!
def subset_sum(sums)
largest_number = subset_sum.sort.reverse[0]
array_without_largest = subset_sum.sort.reverse[1..-1]
full_combination = []
i = 0
while i <= array_without_largest.length
full_combination = full_combination + array_without_largest.combination(i).to_a.to_s
i += 1
end
j = 0
while j <= full_combination.length
return true if full_combination[j].inject { |sum, x| sum + x} == largest_number
j += 1
end
end
return false
end
puts subset_sum(1,2,3,4,10)
puts subset_sum(-1,-3,3,9,8)
Consider this:
def any_subset_adds_to_max?(array)
sub_array = array - [array.max]
every_combination = (1..sub_array.length).flat_map { |n| sub_array.combination(n).to_a }
every_combination.any? { |combination| combination.reduce(:+) == array.max }
end
[
[1, 2, 3, 4, 10],
[-1, -3, 3, 9, 8]
].map { |test_array| any_subset_adds_to_max? test_array } # => [true, false]
Here is the closest example of the code that I could do while maintaining the originality. It works and I appreciate the help!
def subset_sum(sums)
largest_number = sums.max
array_without_largest = sums - [largest_number]
full_combination = []
array_without_largest.size.times do |i|
full_combination << array_without_largest.combination(i+1).to_a
end
full_combination.flatten!(1)
full_combination.size.times do |i|
return true if full_combination[i].inject(:+) == largest_number
end
false
end