Easy ROT13 Ruby "programme" mystery - ruby

I made a simple ROT13 programme and I don't understand one thing:
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
(a.length+1).times do |i|
print a[i + 13]
if i>13
print a[i %14]
end
end
Outputs:
NOPQRSTUVWXYZABCDEFGHIJKLM
If I don't add +1 after a.length, the iteration ends with the letter L. However, if I use print a[i] inside the iteration, it normally starts with A and ends with Z with no +1 addition needed.
Can someone explain this mystery for me?

I just needed a quick rot13 one liner and this SO entry was the first google result. I kept on searching and found a super small one by #xfaider that worked well enough for my purpose.
Just gonna post it here for the next person who wants a one-liner.
string.tr("A-Za-z", "N-ZA-Mn-za-m")

As you may know, the .times loop invokes the block specified number of times, passing into each iteration an incremented value.
If we say 26.times {|i| puts i} it will print values from 0 to 25. Up to, but not including the last value.
Now let's walk through the loop. At first iteration, i is 0. So we print 14th character of the string, "N" (at index 13, zero-based). We don't go into the condition because 0 is not greater than 13. On second iteration we print 15th character, "O". And keep doing this, until we reach i=14.
At this point, the "magic" begins. First, we attempt to print 27th character of the string. There's no such character so we print literally nothing. Then the condition is triggered and we go in.
i % 14 equals 0, so we print zeroth character, "A". Next iteration we print character at index 1 (15 % 14) and so on, until .times finishes its iteration and stops calling the block. Now, for this logic to work, the last value for i must be 26, so that we get 12 in i % 14 and print "M".
Length of the entire string is 26. Remember, .times counts up to but not including the number? That's why we add one to the length, so that it counts from 0 to 26. That's the mystery.
There are many-many ways of improving this code, and you'll learn about them in time. :)
Update
I knew something looked odd about the code. And, of course, there's a bug. When i is 13 we don't print the first time and we don't go into the condition. We waste one iteration. This is a classic example of "off by 1" class of errors. Here's fixed version of code that doesn't waste iterations and contains no mysteries:
a.length.times do |i|
print a[i + 13]
if i > 12
print a[i % 13]
end
end

The length of the string of letters is 26, however the index is 0 based. With this being the case, the letter Z is index number 25. The times method will not run the final iteration(26). Therefore to account for that, we add a +1 to the length.

Related

number of letters to be deleted from a string so that it is divisible by another string

I am doing this problem https://www.spoj.com/problems/DIVSTR/
We are given two strings S and T.
S is divisible by string T if there is some non-negative integer k, which satisfies the equation S=k*T
What is the minimum number of characters which should be removed from S, so that S is divisible by T?
The main idea was to match T with S using a pointer and count the number of instances of T occurring in S when the count is done, bring the pointer to the start of T and if there's a mismatch, compare T's first letter with S's present letter.
This code is working totally fine with test cases they provided and custom test cases I gave, but it could not get through hidden test cases.
this is the code
def no_of_letters(string1,string2):
# print(len(string1),len(string2))
count = 0
pointer = 0
if len(string1)<len(string2):
return len(string1)
if (len(string1)==len(string2)) and (string1!=string2):
return len(string1)
for j in range(len(string1)):
if (string1[j]==string2[pointer]) and pointer<(len(string2)-1):
pointer+=1
elif (string1[j]==string2[pointer]) and pointer == (len(string2)-1):
count+=1
pointer=0
elif (string1[j]!=string2[pointer]):
if string1[j]==string2[0]:
pointer=1
else:
pointer = 0
return len(string1)-len(string2)*count
One place where I think there should be confusion is when same letters can be parts of two counts, but it should not be a problem, because our answer doesn't need to take overlapping into account.
for example, S = 'akaka' T= 'aka' will give the output 2, irrespective of considering first 'aka',ka as count or second ak,'aka'.
I believe that the solution is much more straightforward that you make it. You're simply trying to find how many times the characters of T appear, in order, in S. Everything else is the characters you remove. For instance, given RobertBaron's example of S="akbaabka" and T="aka", you would write your routine to locate the characters a, k, a, in that order, from the start of S:
akbaabka
ak a^
# with some pointer, ptr, now at position 4, marked with a caret above
With that done, you can now recur on the remainder of the string:
find_chars(S[ptr:], T)
With each call, you look for T in S; if you find it, count 1 repetition and recur on the remainder of S; if not, return 0 (base case). As you crawl back up your recursion stack, accumulate all the 1 counts, and there is your value of k.
The quantity of chars to remove is len(s) - k*len(T).
Can you take it from there?

How many times does a zero occur on an odometer

I am solving how many times a zero occus on an odometer. I count +1 everytime I see a zero.
10 -> +1
100-> +2 because in 100 I see 2 zero's
10004 -> +3 because I see 3 zero's
So I get,
1 - 100 -> +11
1 - 500 -> +91
1 - 501 -> +92
0 - 4294967295-> +3825876150
I used rubydoctest for it. I am not doing anything with begin_number yet. Can anyone explain how to calculate it without a brute force method?
I did many attempts. They go well for numbers like 10, 1000, 10.000, 100.000.000, but not for numbers like 522, 2280. If I run the rubydoctest, it will fail on # >> algorithm_count_zero(1, 500)
# doctest: algorithm_count_zero(begin_number, end_number)
# >> algorithm_count_zero(1, 10)
# => 1
# >> algorithm_count_zero(1, 1000)
# => 192
# >> algorithm_count_zero(1, 10000000)
# => 5888896
# >> algorithm_count_zero(1, 500)
# => 91
# >> algorithm_count_zero(0, 4294967295)
# => 3825876150
def algorithm_count_zero(begin_number, end_number)
power = Math::log10(end_number) - 1
if end_number < 100
return end_number/10
else
end_number > 100
count = (9*(power)-1)*10**power+1
end
answer = ((((count / 9)+power)).floor) + 1
end
end_number = 20000
begin_number = 10000
puts "Algorithm #{algorithm_count_zero(begin_number, end_number)}"
As noticed in a comment, this is a duplicate to another question, where the solution gives you correct guidelines.
However, if you want to test your own solution for correctness, i'll put in here a one-liner in the parallel array processing language Dyalog APL (which i btw think everyone modelling mathemathics and numbers should use).
Using tryapl.org you'll be able to get a correct answer for any integer value as argument. Tryapl is a web page with a backend that executes simple APL code statements ("one-liners", which are very typical to the APL language and it's extremely compact code).
The APL one-liner is here:
{+/(c×1+d|⍵)+d×(-c←0=⌊(a|⍵)÷d←a×+0.1)+⌊⍵÷a←10*⌽⍳⌈10⍟⍵} 142857
Copy that and paste it into the edit row at tryapl.org, and press enter - you will quickly see an integer, which is the answer to your problem. In the code row above, you can see the argument rightmost; it is 142857 this time but you can change it to any integer.
As you have pasted the one-liner once, and executed it with Enter once, the easiest way to get it back for editing is to press [Up arrow]. This returns the most recently entered statement; then you can edit the number sitting rightmost (after the curly brace) and press Enter again to get the answer for a different argument.
Pasting teh code row above will return 66765 - that many zeroes exist for 142857.
If you paste this 2 characters shorter row below, you will see the individual components of the result - the sum of these components make up the final result. You will be able to see a pattern, which possibly makes it easier to understand what happens.
Try for example
{(c×1+d|⍵)+d×(-c←0=⌊(a|⍵)÷d←a×+0.1)+⌊⍵÷a←10*⌽⍳⌈10⍟⍵} 1428579376
0 100000000 140000000 142000000 142800000 142850000 142857000 142857900 142857930 142857937
... and see how the intermediate results contain segments of the argument 1428579376, starting from left! There are as many intermediate results as there are numbers in the argument (10 this time).
The result for 1428579376 will be 1239080767, ie. the sum of the 10 numbers above. This many zeroes appear in all numbers between 1 and 1428579376 :-).
Consider each odometer position separately. The position x places from the far right changes once every 10^x times. By looking at the numbers to its right, you know how long it will be until it next changes. It will then hold each value for 10^x times before changing, until it reaches the end of the range you are considering, when it will hold its value at that time for some number of times that you can work out given the value at the very end of the range.
Now you have a sequence of the form x...0123456789012...y where you know the length and you know the values of x and y. One way to count the number of 0s (or any other digit) within this sequence is to clip off the prefix from x.. to just before the first 0, and clip off the suffix from just after the last 9 to y. Look for 0s n in this suffix, and measure the length of the long sequence from prefix to suffix. This will be of a length divisible by 10, and will contain each digit the same number of times.
Based on this you should be able to work out, for each position, how often within the range it will assume each of its 10 possible values. By summing up the values for 0 from each of the odometer positions you get the answer you want.

Behaviour with `repeated_permutation` and floating points

I'm solving Project Euler problem 33.
(10..99).to_a.repeated_permutation(2) do |ary|
print (ary[0]/ary[1]).to_f if ary[0]/ary[1] < 1
end
returns nothing but a series of decimal places and zeros. However if I take the values of the array and divide them manually, for instance:
print 98/99.to_f
the correct value is returned.
Can anyone explain why this happens, and (more importantly) how to circumvent it?
Both ary[0] and ary[1] will always be positive integers inside your block. That means that ary[0]/ary[1] will be 0 or some positive integer. Your block does nothing when ary[0]/ary[1] is less than one (i.e. when ary[0]/ary[1] is anything other than zero). Since you only call print when ary[0]/ary[1] == 0, you're really just saying:
print 0.to_f if ary[0]/ary[1] == 0
This:
print 98/99.to_f
prints what you're expecting but you're not doing that in the block, you're saying things like:
print (98/99).to_f
and we see that the to_f call is in the wrong place.
You're probably trying to say:
print ary[0]/ary[1].to_f if ary[0]/ary[1] < 1
to get floating point division rather than integer division.

Nested each loop not working

I'm going through problems at Project Euler to gain some experience with Ruby. The problem I'm working on now has me looking at a 1000-digit number and finding the sequence of five consecutive digits with the greatest product.
big_num = //1000-digit number
num_array = big_num.split('').map(&:to_i)
biggest = 0
prod = 1
(0..995).each do |x|
(0..4).each do |y|
prod *= num_array[x+y]
end
biggest = prod if prod > biggest
prod = 0
end
puts biggest
This gives me 882, which is incorrect. To look for the problem, I had it print the values of x and y for each iteration. After the first iteration, it always prints out the five values of y as 7,3,1,6,7, which, when all multiplied together, equal 882. So, after the first iteration of the outer loop, it's not looking past the first five values of num_array, even though x seems to be incrementing properly. For the life of me, I can't figure out why it's behaving this way.
I would use the nice Enumerable method each_cons() to get all sequences of consecutive numbers, eliminating your outer loop. Secondly, I would use reduce(:*) to get each product, eliminating your inner loop. Finally I would call .max to get your answer.
num_array.each_cons(5).map{|seq| seq.reduce(:*)}.max
Voilà, one-liner.
You have to reinitialize prod with 1 instead of 0.

Ruby - Picking an element in an array with 50% chance for a[0], 25% chance for a[1]

Nothing too complicated, basically I just want to pick an element from the array as if I were making coin tosses for each index and and choosing the index when I first get a head. Also no heads means I choose the last bin.
I came up with the following and was wondering if there was a better/more efficient way of doing this.
def coin_toss(size)
random_number = rand(2**size)
if random_number == 0
return size-1
else
return (0..size-1).detect { |n| random_number[n] == 1 }
end
end
First guess...pick a random number between 1 and 2**size, find the log base 2 of that, and pick the number that many elements from the end.
Forgive my horrible ruby skillz.
return a[-((Math.log(rand(2**size-1)+1) / Math.log(2)).floor) - 1]
if rand returns 0, the last element should be chosen. 1 or 2, the next to last. 3, 4, 5, or 6, third from the end. Etc. Assuming an even distribution of random numbers, each element has twice as much chance of being picked as the one after it.
Edit: Actually, it seems there's a log2 function, so we don't have to do the log/log(2) thing.
return a[-(Math.log2(rand(2**size - 1)+1).floor) - 1]
You may be able to get rid of those log calls altogether like
return a[-((rand(2**size-1)+1).to_s(2).length)]
But you're creating an extra String. Not sure whether that's better than complicated math. :)
Edit: Actually, if you're going to go the string route, you can get rid of the +1 and -1 altogether. It'd make the probabilities more accurate, as the last two elements should have an equal chance of being chosen. (If the next-to-last value isn't chosen, the last value would always be.)
Edit: We could also turn the ** into a bit shift, which should be a little faster (unless Ruby was smart enough to do that already).
return a[-(rand(1<<size).to_s(2).length)]
A non-smart, simple way is:
def coin_toss( arr )
arr.detect{ rand(2) == 0 } || arr.last
end

Resources