Algorithm to rank numeric passcode? - ruby

I'm studying a (mobile) app where I need to get a user PIN:
a numeric "passcode" of 6/8 ciphers, input with something like this UI:
So, in a registration step, the user configure his one passcode (as it would be a password).
Let say the passcode must have a fixed size (say 8 ciphers: ********)
My question is related to a possible algorithm to verify/check the number that user choose, giving a bad rank in case of repeated ciphers or standard cipher patterns (12345678, 00001111), easily predicible by a malicious crackers...
Any idea for such an algorithm ?
At firs glance the algorithm could discourage (bad rank) a passcod containing repeated ciphers, simething like:
00117788
88886611
or "usual" ascending/descending patterns as:
12345678
98765432
Or numeric patterns related to personal, by example in my case, I'm born in 02 September 1963, so it could be a bad idea to have as passcode:
02091963
Instead, sequence that appear to me as "good" could be by example these one:
18745098
90574808
07629301
Collateral question: do you think that a numeric passcode of let say 8 ciphers could be an acceptable solution as "password" to validate a payment transaction ?
BTW, I'm coding in Ruby.
thanks for your patience!
giorgio

For your first 2 cases:
Number of repeated consecutive characters in the string:
str = "00117788"
str.chars.each_cons(2).select {|a,b| a == b}.count
#=> 4
Or as #CarySwoveland pointed out this will have the same result
str.size - str.squeeze.size
#=> 4
Number of incremented characters
str = "12345678"
str.chars.map(&:to_i).each_slice(2).select {|a,b| (a + 1) == b || (a - 1) == b }.count
#=> 4
#note this will also return 4 for "12563478"
# you could also use str.chars.map(&:to_i).each_cons(2).select {|a,b| (a + 1) == b || (a - 1) == b }.count
# This will return 7 for "12345678" and still return 4 for "12563478"
You could combine the above 2 as well like
str = "00117788"
str.chars.map(&:to_i).each_cons(2).select {|a,b| (a + 1) == b || (a - 1) == b || a == b }.count
#=> 6
As for the "personal" issue if you have the birth day then something as simple as this should work:
require 'date'
birth_day = Date.new(1963,9,2)
str = "02091963"
str == birth_day.strftime("%d%m%Y")
#=> true
Although for the last one I would suggest comparing multiple formats e.g. %Y%m%d and %m%d%Y etc. you could even do something like
str.chars.sort == birth_day.strftime("%d%m%Y").chars.sort
#=> true
To make sure they don't just use those numbers in some jumbled format.
Hopefully this would get you started since I don't know what your thresholds are for "good" and "bad" these are just suggestions for checking the values. Although it seems the definition for "good" should just be not "bad". Sort of like a validity check.
If I were to suggest a score of < 4 using methods 1 and 2 (or the combination method) && not an assortment of birth_day numbers would probably be sufficient e.g.
def tester(str,birth_date)
return false if ![6,8].include?(str.size)
b_day = birth_date.strftime("%Y%m%d").chars.sort
str.chars.map(&:to_i).each_cons(2).select do |a,b|
(a + 1) == b ||
(a - 1) == b ||
a == b
end.count < 4 && b_day != str.chars.sort
end
tester("00112233",Date.new(1963,9,2))
#=> false
tester("18745098",Date.new(1963,9,2))
#=> true
Seems like it works with your examples
arry = ["00117788","88886611","12345678","98765432","02091963","18745098","90574808","07629301"]
Hash[arry.map{|v| [v,tester(v,Date.new(1963,9,2))]}]
#=>=> {"00117788"=>false, "88886611"=>false,
"12345678"=>false, "98765432"=>false,
"02091963"=>false, "18745098"=>true,
"90574808"=>true, "07629301"=>true}

Related

How do the two R codes, which should do the same thing (at least in my head), differ?

In order to replicate this code, you will need these packages:
Tidyverse, Nycflights13
I am basically trying to understand why two pieces of code, which in my head should do the same thing, don't do the same thing.
I am currently learning R for data science from R for Data Science by Garrett Grolemund & Hadley Wickham, and I've gotten to a point which the code begins to confuse me quite a bit, which hopefully is normal! I will write down the two pieces of code which confuse me in why they don't do the same thing!
filter(flights, dest == c("HOU","IAH"))
#and
filter(flights, dest == "HOU" | dest == "IAH")
I expected both of these codes to show the same amount of rows but the first one shows 4658 rows (the wrong amount), where as the second one shows 9313 (the right amount).
What I wanted to do is to shorten the code by using (filter(flights, dest == c("HOU","IAH"))) instead of (filter(flights, dest == "HOU" | dest == "IAH"))
but it yields different results, which gravely confuses me!
Please give me your advice, I am a newbie!
Because filter(flights, dest == c("HOU","IAH")) is the same as
filter(flights, dest == c("HOU","IAH"))
flights$dest = c("HOU","IAH")
* # For demonstation purposes, I'm assuming the flights dataset has has 4 rows, as:
c("HOU","CPH","IAH","EDI") == c("HOU","IAH")
Now, a vector of 4 elements cannot possible be the same as a vector of 2 elements. And this performs an element-wise comparison, "HOU" == "HOU", "CPH" == "IAH", "IAH" == ???. So R "helps" us by repeating the shorter vector. The output is thus:
> c("HOU","CPH","IAH","EDI") == c("HOU","IAH")
[1] TRUE FALSE FALSE FALSE
Try extending with a 5th element:
> c("HOU","CPH","IAH","EDI", "LDN") == c("HOU","IAH")
[1] TRUE FALSE FALSE FALSE FALSE
Warning message:
In c("HOU", "CPH", "IAH", "EDI", "LDN") == c("HOU", "IAH") :
longer object length is not a multiple of shorter object length
and you broke it.
So the 1st line only makes sense if and only if flights has 2 rows, whereupon an element-wise comparison can be performed.
However, what you might be looking for is the %in% operator:
> c("HOU","CPH","IAH","EDI") %in% c("HOU","IAH")
[1] TRUE FALSE TRUE FALSE
> c("HOU","CPH","IAH","EDI","LDN") %in% c("HOU","IAH")
[1] TRUE FALSE TRUE FALSE FALSE
which can be expanded to filter(flights, dest %in% c("HOU","IAH")) and luckily works for vectors of any length.

Redis: sorting hash "fields" in alpha

I am trying to sort the "fields" in a hash.
For example,
mykey, cde, firstone
mykey, abcde, secondone
mykey, bcde, thirdone
I want to sort the fields(cde, abcde, bcde) in alphabet order, but there is no way to do so.. If anyone knows about this, please help me.
If there is no way to solve this, I am thinking about changing names of key&values.. and use zadd instead of hash. If you have a better solution, please give me an advice here.
Hash field names are not sortable-by easily - there is no native command to do so and the order in which fields are returned (e.g. with HGETALL) is for all intents and purposes random.
While Sorted Sets are preferable when it comes to sorting, you could work around this with use of a Lua script that will perform lexical sorting the Hash's fields. For example:
$ cat hashsort.lua
local r = redis.call('HGETALL',KEYS[1])
local t = {}
for i=1,#r,2 do
t[#t+1] = { field = r[i], value = r[i+1] }
end
table.sort(t, function(a,b) return a.field < b.field end)
r = {}
for _, v in pairs(t) do
r[#r+1] = v.field
r[#r+1] = v.value
end
return r
$ redis-cli HMSET hash z 99 ee 55 e 5 a 1 b 2 ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ justsomethinglongsohashmaxzipwillbeexceeded
OK
$ redis-cli --eval hashsort.lua hash
1) "a"
2) "1"
3) "b"
4) "2"
5) "e"
6) "5"
7) "ee"
8) "55"
9) "z"
10) "99"
11) "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
12) "justsomethinglongsohashmaxzipwillbeexceeded"

Optimizing Array Memory Usage

I currently have a very large array of permutations, which is currently using a significant amount of RAM. This is the current code I have which SHOULD:
Count all but the occurrences where more than one '1' exists or three '2's exist in a row.
arr = [*1..3].repeated_permutation(30).to_a;
count = 0
arr.each do |x|
if not x.join('').include? '222' and x.count(1) < 2
count += 1
end
end
print count
So basically this results in a 24,360 element array, each of which have 30 elements.
I've tried to run it through Terminal but it literally ate through 14GB of RAM, and didn't move for 15 minutes, so I'm not sure whether the process froze while attempting to access more RAM or if it was still computing.
My question being: is there a faster way of doing this?
Thanks!
I am not sure what problem you try to solve. If your code is just an example for a more complex problem and you really need to check programatically every single permumation, then you might want to experiment with lazy:
[*1..3].repeated_permutation(30).lazy.each do ||
# your condition
end
Or you might want to make the nested iteratior very explicit:
[1,2,3].each do |x1|
[1,2,3].each do |x2|
[1,2,3].each do |x3|
# ...
[1,2,3].each do |x30|
permutation = [x1,x2,x3, ... , x30]
# your condition
end
end
end
end
end
But it feels wrong to me to solve this kind of problem with Ruby enumerables at all. Let's have a look at your strings:
111111111111111111111111111111
111111111111111111111111111112
111111111111111111111111111113
111111111111111111111111111121
111111111111111111111111111122
111111111111111111111111111123
111111111111111111111111111131
...
333333333333333333333333333323
333333333333333333333333333331
333333333333333333333333333332
333333333333333333333333333333
I suggest to just use enumerative combinatorics. Just look at the patterns and analyse (or count) how often your condition can be true. For example there are 28 indexes in your string at which a 222 substring could be place, only 27 for the 2222 substring... If you place a substring how likely is it that there is no 1 in the other parts of the string?
I think your problem is a mathematics problem, not a programming problem.
NB This is an incomplete answer, but I think the idea might give a push to the proper solution.
I can think of a following approach: let’s represent each permutation as a value in ternary number base, padded by zeroes:
1 = 000..00001
2 = 000..00002
3 = 000..00010
4 = 000..00011
5 = 000..00012
...
Now consider we restated the original task, treating zeroes as ones, ones as twos and twos as threes. So far so good.
The whole list of permutations would be represented by:
(1..3**30-1).map { |e| x = e.to_s(3).rjust(30, '0') }
Now we are to apply your conditions:
def do_calc permutation_count
(1..3**permutation_count-1).inject do |memo, e|
x = e.to_s(3).rjust(permutation_count, '0')
!x.include?('111') && x.count('0') < 2 ? memo + 1 : memo
end
Unfortunately, even for permutation_count == 20 it takes more than 5 minutes to calculate, so probably some additional steps are required. I will be thinking of further optimization. Currently I hope this will give you a hint to find the good approach yourself.

Can someone explain how this ruby basic-calculator code handles addition and subtraction?

So I was working on a Codewars problem here, and found some code posted to Github that works out-of-the-box. Problem is, I don't understand how part of it works. Here are the Codewars directions:
Description:
Create a simple calculator that given a string of operators (+ - * and /) and numbers separated by spaces returns the value of that expression
Example:
Calculator.new.evaluate("2 / 2 + 3 * 4 - 6") # => 7
Remember about the order of operations! Multiplications and divisions have a higher priority and should be performed left-to-right. Additions and subtractions have a lower priority and should also be performed left-to-right.
Here's the code:
class Calculator
def evaluate(string)
operator_stack = []
number_stack = []
string.split(" ").each do |token|
if /\d/.match(token)
number_stack << token.to_i
elsif operator_stack.length > 0 && /[*]|[\/]/.match(operator_stack[-1])
x, y = number_stack.pop, number_stack.pop
temp_result = y.send(operator_stack.pop, x)
number_stack << temp_result
operator_stack << token
else
operator_stack << token
end
end
while(number_stack.length > 0 && operator_stack.length > 0)
x, y = number_stack.shift, number_stack.shift
temp_result = x.send(operator_stack.shift,y)
number_stack.unshift(temp_result)
end
return number_stack[0]
end
end
Now I've learned enough Ruby that I can read through and understand what the various functions do, but when it comes to the mathematical operations the code does, I don't see where or how it handles addition and subtraction. There is some regex that's used to match for multiplication and division present in this line:
elsif operator_stack.length > 0 && /[*]|[\/]/.match(operator_stack[-1])
But since I don't see the plus or minus sign anywhere in the code, I don't get how it performs those operations. Can anyone help?
BTW, I'm done with the Codewars problem and have moved on. I also discovered you can solve this calculator problem with "instance_eval string", which blew my mind when I first saw it. But, it makes sense after reading through what I found here. I should have guessed that there was a one-liner that would work as a basic calculator :)
I would still like to know how this code handles addition and subtraction. Can anyone enlighten me?
The actual operations are performed in these lines:
temp_result = y.send(operator_stack.pop, x)
and later
temp_result = x.send(operator_stack.shift,y)
which says "send the operator_stack.shift/pop message with parameter y to objectx, which is basically the same as doing x <operator> y where <operator> is the operator on top of operator_stack

Compare multiple variables with a value in a single expression

I have two variables a and b. I want to compare both a and b to a value, say 10.
I can do it like this:
10 == a && 10 == b
But, I was wondering if there is any way to write it in a single expression? (E.g. like a == b == 10)
[a,b,3].all? {|x| x==10}
but in this case
[].all? {|x| x==10}
will also return true
Updated, after comment from aztaroth:
[a,b].uniq == [10]

Resources