pythagorean triangle with any side equal to zero - ruby

So I have this spec I'm trying to solve for (I did not write the spec)...
it "returns false if any of the arguments are 0" do
# [0, 1, 1].permutation(3) returns all permutations of [0, 1, 1]
length = rand(0.01..100.0)
[0, length, length].permutation(3).all? { |(a,b,c)| valid_triangle?(a,b,c) }.should be_false
end
Here is my code
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two=three
return "true"
else
return "false"
end
else
return "false"
end
end
How can I pass my spec? What am I missing?

The main problem is that when the test permutes the values of a, b and c, your method is not always checking that it is the square of the hypotenuse that equals the sum of the squares of the other two sides. For example, if a=3, b=4 and c=5, one of your tests will be 4*4 + 5*5 == 3*3. You need to sort a, b and c before checking the sum of squares, which is a good idea anyway, since the position of the hypotenuse among the parameters is not guaranteed. You could also simplify your code somewhat. Here's one way you could write it:
TOLERANCE = 0.01
def right_triangle?(a, b, c)
return false if a == 0 || b == 0 || c == 0
a, b, c = [a,b,c].sort
(a**2 + b**2 - c**2).abs < TOLERANCE
end
length = rand(0.01..100.0)
[0.0, length, length].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c)}
#=> false
[3,4,5].permutation(3).all? { |(a,b,c)| right_triangle?(a,b,c) }
#=> true
Since you are dealing with floats, you need to establish some level of tolerance when comparing values for equality. I've used an arbitrary fixed amount (0.01) for demonstration purposes.

You are returning "false" and "true" instead of false and true, also you check that one+two=three, when you should check one+two==three (an equals check, not an assignment)
def valid_triangle?(a, b, c)
#A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2.
if a > 0 && b > 0 && c > 0
one = a**2
two = b**2
three = c**2
if one+two == three
return true
else
return false
end
else
return false
end
end

Related

Find a contiguous sub-array that adds up to a target number and prints out the sub array using divide and conquer [duplicate]

I will be happy to get some help.
I have the following problem:
I'm given a list of numbers seq and a target number and I need to write 2 things:
A recursive solution that returns True if there is a sum of a subsequence that equals the target number and False otherwise.
example:
subset_sum([-1,1,5,4],0) # True
subset_sum([-1,1,5,4],-3) # False
Secondly, I need to write a solution using what I wrote in the previous solution
but now with memoization that uses a dictionary in which the keys are tuples:
(len(seq),target)
For number 1 this is what I got to so far:
def subset_sum(seq, target):
if target == 0:
return True
if seq[0] == target:
return True
if len(seq) > 1:
return subset_sum(seq[1:],target-seq[0]) or subset_sum(seq[1:],target)
return False
Not sure I got it right so if I could get some input I will be grateful.
For number 2:
def subset_sum_mem(seq, target, mem=None ):
if not mem:
mem = {}
key=(len(seq),target)
if key not in mem:
if target == 0 or seq[0]==target:
mem[key] = True
if len(seq)>1:
mem[key] = subset_sum_mem(seq[1:],target-seq[0],mem) or subset_sum_mem(seq[1:],target,mem)
mem[key] = False
return mem[key]
I can't get the memoization to give me the correct answer so I'd be glad for some guidance here.
Thanks for anyone willing to help!
Just for reference, here's a solution using dynamic programming:
def positive_negative_sums(seq):
P, N = 0, 0
for e in seq:
if e >= 0:
P += e
else:
N += e
return P, N
def subset_sum(seq, s=0):
P, N = positive_negative_sums(seq)
if not seq or s < N or s > P:
return False
n, m = len(seq), P - N + 1
table = [[False] * m for x in xrange(n)]
table[0][seq[0]] = True
for i in xrange(1, n):
for j in xrange(N, P+1):
table[i][j] = seq[i] == j or table[i-1][j] or table[i-1][j-seq[i]]
return table[n-1][s]
This is how I'd write the subset_sum:
def subset_sum(seq, target):
if target == 0:
return True
for i in range(len(seq)):
if subset_sum(seq[:i] + seq[i+1:], target - seq[i]):
return True
return False
It worked on a couple of examples:
>>> subset_sum([-1,1,5,4], 0))
True
>>> subset_sum([-1,1,5,4], 10)
True
>>> subset_sum([-1,1,5,4], 4)
True
>>> subset_sum([-1,1,5,4], -3)
False
>>> subset_sum([-1,1,5,4], -4)
False
To be honest I wouldn't know how to memoize it.
Old Edit: I removed the solution with any() because after some tests I found out that to be slower!
Update: Just out of curiosity you could also use itertools.combinations:
from itertools import combinations
def com_subset_sum(seq, target):
if target == 0 or target in seq:
return True
for r in range(2, len(seq)):
for subset in combinations(seq, r):
if sum(subset) == target:
return True
return False
This can do better that the dynamic programming approach in some cases but in others it will hang (it's anyway better then the recursive approach).
I have this modified code:
def subset_sum(seq, target):
left, right = seq[0], seq[1:]
return target in (0, left) or \
(bool(right) and (subset_sum(right, target - left) or subset_sum(right, target)))
def subset_sum_mem(seq, target, mem=None):
mem = mem or {}
key = (len(seq), target)
if key not in mem:
left, right = seq[0], seq[1:]
mem[key] = target in (0, left) or \
(bool(right) and (subset_sum_mem(right, target - left, mem) or subset_sum_mem(right, target, mem)))
return mem[key]
Can you provide some test cases this does not work for?

special pythagorean triplets

def pythagorean(n):
aAndB = []
for a in range(150, n-1):
for b in range(150, n):
for c in range(150,n+1):
if (c * c) == a *a + b*b and a + b + c == 1000:
aAndB.append(a)
return aAndB
print(pythagorean(500))
So I made this function to find pythagorean triplets that meets criteria a+b+c=1000. When I run this, I get [200,375]. Question is why do I receive two numbers in my list aAndB when I specifically asked to append an item for a?
If I try it with aAndB.append(c), the result shows [425, 425]. How do I fix it only to show exactly one element in the list?
Thank you for your help!
That's because there are 2 values, that satisfy your condition:
if (c * c) == a *a + b*b and a + b + c == 1000:
You can debug the code or just add more information in array, like that:
def pythagorean(n):
aAndB = []
for a in range(150, n-1):
for b in range(150, n):
for c in range(150,n+1):
if (c * c) == a * a + b * b and a + b + c == 1000:
aAndB.append({'a': a, 'b': b, 'c': c})
return aAndB
result = pythagorean(500)
for v in result:
print(v)
So if you want just one element - choose which one from 'result' array.
For example, if you want only first:
first_element = None
if len(result) > 0:
first_element = result[0]
print('First element:', first_element)
You can use euclid’s proof of pythagoreas triplets. You can choose any arbitrary numbers greater than zero say m,n. According to euclid the triplet will be a(m∗m−n∗n),b(2∗m∗n),c(m∗m+n∗n). Now apply this formula to find out the triplets , say our one value of triplet is 6 then, other two.
a(m∗m−n∗n),b(2∗m∗n),c(m∗m+n∗n)
It is sure that b(2∗m∗n) is obviously even. So now
(2∗m∗n)=6 =>(m∗n)=3 =>m∗n=3∗1 =>m=3,n=1
You can take any other value rather than 3 and 1 but those two values should hold the product of two numbers is 3 (m∗n=3).
Now , when m equals 3 and n equals 1 then
a(m∗m−n∗n)=(3∗3−1∗1)=8 , c(m∗m−n∗n)=(3∗3+1∗1)=10
6,8,10 is our triplet for value this our visualization of how generating triplets .
If given number is odd like (9) then slightly modified here, because b(2∗m∗n) never be odd. So, here we have to take
a(m∗m−n∗n)=7 , (m+n)∗(m−n)=7∗1 , So,(m+n)=7,(m−n)=1
Now find m and n from here then find other two values.
Do code according this , it will generate distinct triplets and efficiently

Identifying Triangle with if/else

Question is a user gives 3 sides and identifies triangles, like equilateral, isosceles and scalene. Here is my coding, I don't know why gives any sides that always show up "invalid". I think it's logic wrong, but I can't figure out.
puts "please input the length of 3 sides:"
a = gets.chomp.to_i
b = gets.chomp.to_i
c = gets.chomp.to_i
if a + b <= c
puts "invalid"
elsif a <= 0 || b <= 0 || c <= 0
puts "invalid"
else
if a == b && b == c
puts"equilateral triangle"
elsif a == b
puts"isosceles triangle"
else
puts"scalene triangle"
end
end
The fact that your code always prints "invalid" makes me think that input is passed in on one line instead of being on separate lines. For example, when the input is:
50 50 50
instead of getting 50 in all three variables you would get 50 in a and 0 in b, c. This is because gets takes in an entire line instead of taking one value.
In such an event, this is what you need:
a, b, c = gets.split.map{ |value| value.to_i }
A better more effective way to do this is to store the values of the triangle sides into a hash first, the value of of each triangle side will be the keys, and the value of each key can be the repeats. This will work with strings too.
Here is an Example.
# First you get an array, you can use gets.chomp as string and split to array, whichever way you choose, but in the end we end up with an array, and we pass the array to the method.
def triangle_type(arr)
# Create new empty hash
repeated_sides = Hash.new(0)
# First make sure the array is only a length of three. (this is optional)
if arr.length == 3
# Iterate through each value in the array and store it to to a hash to find duplicates
arr.each do |x|
repeated_sides[x] += 1
end
# sort the hash by it's values in descending order, for logic to work later.
repeated_sides = repeated_sides.sort_by {|k,v| v}.reverse.to_h
# uncomment this below to see the duplicate sides hash
#puts "#{repeated_sides}"
# Iterate through the sorted hash, apply logic starting from highest and first value the iterator will find.
repeated_sides.each do |k,v|
return v == 3 ? 'Equilateral Triangle' : v == 2 ? 'Isosceles Triangle' : 'Scalene Triangle'
end
end
# Return Not a triangle if the condition fails
return 'Not a triangle'
end
# Test with integers
puts triangle_type([4,1,2,5]) # output: Not a triangle
puts triangle_type([3,3,3]) # output: Equilateral Triangle
puts triangle_type([4,3,3]) # output: Isosceles Triangle
puts triangle_type([4,2,3]) # output: Scalene Triangle
# Test with strings
puts triangle_type(['4','1','2','5']) # output: Not a triangle
puts triangle_type(['3','3','3']) # output: Equilateral Triangle
puts triangle_type(['4','3','3']) # output: Isosceles Triangle
puts triangle_type(['4','2','3']) # output: Scalene Triangle
puts triangle_type(['a','a','a']) # output: Equilateral Triangle
puts triangle_type(['a','c','c']) # output: Isosceles Triangle
puts triangle_type(['a','b','c']) # output: Scalene Triangle
Skipping user inputs, since I can not reproduce the error (even if Unihedron found a fix) there is still a problem with the logic.
When the input is a = 1000, b = 1, c = 1, the result is "scalene triangle", but it should return "invalid". Below a fix I suggest.
Let's store the input in an array (already converted into integer or float):
sides = [a, b, c]
First you need to check that all sides are positive:
sides.all? { |x| x > 0 }
Then, check that the sum of two sides is greater than the other:
sides.combination(2).map{ |x| x.sum }.zip(sides.reverse).all? { |xy, z| xy > z }
Finally (I'm missing something?), to pick the triangle denomination you can use an hash accessing it by sides.uniq result:
triangle_kinds = {1 => 'equilateral', 2 => 'isosceles', 3 => 'scalene'}
triangle_kinds[sides.uniq.size]
Used the following methods over array (enumerable):
https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-all-3F
https://ruby-doc.org/core-2.5.1/Array.html#method-i-combination
https://ruby-doc.org/core-2.5.1/Array.html#method-i-map
https://ruby-doc.org/core-2.5.1/Array.html#method-i-zip
https://ruby-doc.org/core-2.5.1/Array.html#method-i-reverse
https://ruby-doc.org/core-2.5.1/Array.html#method-i-sum
https://ruby-doc.org/core-2.5.1/Array.html#method-i-uniq

Can someone explain the mathematics behind this solution

A question asks:
Take a sequence of numbers from 1 to n (where n > 0).
Within that sequence, there are two numbers, a and b.
The product of a and b should equal the sum of all numbers in the sequence excluding a and b.
Given a number n, could you tell me the numbers excluded from the sequence?
My plan was to get the sum of the range, then create an array using the combination enumerator to get all of the possible pairs of the range, then check if the product of the pair equals the sum of the range minus the sum of the pair. This solution worked, but took way too long:
def removNb(n)
arr = [*1..n]
sum = arr.inject(:+)
ab = []
[*(n/2)..n].combination(2).to_a.each do |pair|
if pair.inject(:*) == sum - pair.inject(:+)
ab << pair
ab << [pair[1],pair[0]]
end
end
ab
end
Here is a solution that I found:
def removNb(n)
res = []
total = (n*n + n) / 2
range = (1..n)
(1..n).each do |a|
b = ((total - a) / (a * 1.0 + 1.0))
if b == b.to_i && b <= n
res.push([a,b.to_i])
end
end
return res
end
but can't understand how it works. I understand the equation behind the total.
You could form a equation
a * b = (sum of sequence from 1 to n) - (a + b)
from this statement
the product of a and b should be equal to the sum of all numbers in
the sequence, excluding a and b
sum of sequence from 1 to n (denote as total) = n(n+1)/2 = (n*n + n) / 2
Reorder above equation, you get
b = (total - a) / (a + 1)
The remaining work is to test if there exist integer a and b matching this equation
The code returns an array of all pairs of numbers in the sequence that have the desired property. Let's step through it.
Initialize the array to be returned.
res = []
Compute the sum of the elements in the sequence. The sum of the elements of any arithmetic sequence equals the first element plus the last element, multiplied by the number of elements in the sequence, the product divided by 2. Here that is total = n*(1+n)/2, which can be expressed as
total = (n*n + n) / 2
range = (1..n) is unnecessary as range is not subsequently referenced.
Loop through the elements of the sequence
(1..n).each do |a|
For each value of a we seek another element of the sequence b such that
a*b = total - a - b
Solving for b:
b = (total - a)/ (a * 1.0 + 1.0)
If b is in the range, save the pair [a, b]
if b == b.to_i && b <= n
res.push([a,b.to_i])
end
Return the array res
res
This method contains two errors:
If [a,b] is added to res, it will be added twice
[a,a] could be added to res (such as n=5, a=b=3)
I would write this as follows.
def remove_numbers(n)
total = n*(n+1)/2
(1..n-1).each_with_object([]) do |a,res|
next unless (total-a) % (a+1) == 0
b = (total-a)/(a+1)
res << [a,b] if (a+1..n).cover?(b)
end
end
For example,
remove_numbers 10
#=> [[6, 7]]
remove_numbers 1000
#=> []
Out of cursiosity:
(2..10_000).map { |x| [x, remove_numbers(x).size] }.max_by(&:last)
#=> [3482, 4]
remove_numbers 3482
#=> [[1770, 3423], [2023, 2995], [2353, 2575], [2460, 2463]]

Trying to check if two strings are permutations of each other

I am trying to implement this function that checks if two strings are permutations of each other. The code itself is straightforward.
def permutation(a, b)
if a.length != b.length
return False
end
a = a.chars.sort.join
b = b.chars.sort.join
return a == b
end
a = "abcedff"
b = "acbedf"
puts (permutation(a, b).to_s)
However, when I try to run this file on the terminal, I keep getting an error that says
permutation.rb:3:in permutation': uninitialized constant False (NameError)
from permutation.rb:13:in'
I don't understand the reason for this.
Ruby is not Python. You want true and false, not True and False.
Don't over-complicate it. All you need to do is compare two character arrays. For example:
def permutation a, b
a.chars.sort == b.chars.sort
end
Given your corpus as posted, this yields:
a = "abcedff"
b = "acbedf"
permutation a, b
#=> false
permutation a, a
#=> true

Resources