Ruby, User id with 6 random numbers [closed] - ruby-on-rails

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
For example: If I'm creating a new user in Rails, the id number will be 1, 2, 3 and so on. In the URL the users id will be like "users/1". My question is: How do I make a random number for every new users? So the id won't be 1, 2, 3 and so on. More like id: 643893, name: Carl, age: 34?

If you really insist upon the digits, you could do something like this
def random_id
Random.new.bytes(8).bytes.join[0,8]
end
5.times { puts random_id }
Output
19854517
11802301
16391219
18414719
35583913
Anyway, I believe you have a bit of an XY Problem and your end goal is to just generate unique ids for your users. The function above is going to be random but without checking with your database, you'll have no idea whether the result is unique or not. Here's some alternative approaches I'd take:
If you wanted, it's not 8 digits, but you could slice of the beginning of a uuid
require "securerandom"
SecureRandom.uuid.split("-").first
# b05582d8
That said, I think it's a much better to use the whole thing
SecureRandom.uuid
# b05582d8-3489-4bba-b94f-565ee458b1f9

Since you asked for user ids, I'm going to assume
There should be no collisions in the user ids.
Because of this, you don't want just purely random ids, otherwise you'll end up with a collision as a result of the birthday problem. Out of only 10^8 random userids, this means the odds are significant that a collision will occur after around 10^4 users, and it is always possible it may happen earlier than this.
What you are looking for is a permutation function. Using some basic number theory, here's one simple, non-secure way to do it:
N = 99999999 # set of ordered numbers to permute = {0,1,2,...,N-1}
P = 1000000007 # prime number larger than N
A = 181932213 # random integer: 2 <= A < P
B = 611232645 # random integer: 0 <= B < P
def get_user_id( i )
j = i.instance_of?(Fixnum) ? i : i.dup # i.dup if i is a Bignum
j -= 1
begin j = ( A * j + B ).modulo P end until j < N
j + 1
end
Internally, you still want to generate incremental ids id. Then get_user_id(id) with return an 8-digit number (or less) which you can use as the user_id, which is guaranteed to not conflict with any other user_id for all non-zero 8-digit numbers id.
To illustrate:
1.upto(N) do |i|
j = get_user_id(i)
puts "#{i}\t#{j}"
end
Output:
1 41168842
2 45894710
3 11122313
4 74043578
5 78434518
6 72415977
...
Note that anyone who knows/suspects you are using this algorithm can fairly easily determine what N, P, A, and B are from getting some consecutively generated values, and obtain the underlying id value which you wanted to keep hidden. If you need something more secure than this, then consider looking into permutation ciphers.

Below is the example, which start with alphabet.
def self.generate_reg_no
#reg_no = PatientReg.last
#reg_no.blank? ? (#reg_no = 'REG00') : (#reg_no = #reg_no.reg_no)
new_id =""
n = #reg_no.length
last_count = #reg_no.slice!(4..n)
new_id << "REG0" << (last_count.to_i+1).to_s
end
You can modify as per you requirement.

Related

Finding the number of digits in a number restricted number of tools since I am a Python beginner

def digits(n):
total=0
for i in range(0,n):
if n/(10**(i))<1 and n/(10**(i-1))=>1:
total+=i
else:
total+=0
return total
I want to find the number of digits in 13 so I do the below
print digits(13)
it gives me $\0$ for every number I input into the function.
there's nothing wrong with what I've written as far as I can see:
if a number has say 4 digits say 1234 then dividing by 10^4 will make it less than 1: 0.1234 and dividing by 10^3 will make it 1.234
and by 10^3 will make it 1.234>1. when i satisfies BOTH conditions you know you have the correct number of digits.
what's failing here? Please can you advise me on the specific method I've tried
and not a different one?
Remember for every n there can only be one i which satisfies that condition.
so when you add i to the total there will only be i added so total returning total will give you i
your loop makes no sense at all. It goes from 0 to exact number - not what you want.
It looks like python, so grab a solution that uses string:
def digits(n):
return len(str(int(n))) # make sure that it's integer, than conver to string and return number of characters == number of digits
EDIT:
If you REALLY want to use a loop to count number of digits, you can do this this way:
def digits(n):
i = 0
while (n > 1):
n = n / 10
++i
return i
EDIT2:
since you really want to make your solution work, here is your problem. Provided, that you call your function like digits(5), 5 is of type integer, so your division is integer-based. That means, that 6/100 = 0, not 0.06.
def digits(n):
for i in range(0,n):
if n/float(10**(i))<1 and n/float(10**(i-1))=>1:
return i # we don't need to check anything else, this is the solution
return null # we don't the answer. This should not happen, but still, nice to put it here. Throwing an exception would be even better
I fixed it. Thanks for your input though :)
def digits(n):
for i in range(0,n):
if n/(10**(i))<1 and n/(10**(i-1))>=1:
return i

finding the phone number whose digits sum to the largest number in Ruby [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
This is my first time using Ruby and i need to take an array with three 10 digits phone numbers and have the program give me the phone number whose sum is the greatest.
my_array = (['123-456-7777', '963-481-7945', '111-222-3333'])
=> '963-481-7945'
I have no idea where to start.
Please help!
I am currently doing the codeacademy course on Ruby. But so far I don't know enough to get through this problem.
I thought about starting with
my_array = (['123-456-7777', '963-481-7945', '111-222-3333'])
my_array[2]
which would give me the desired answer of
=> "963-481-7945"
but i know that's not the way to go.
I was thinking so performing a sum for each value and then setting it equal to the set of number I want to display but I'm not sure how to set that up.
The solution would something like this:
my_array = (['123-456-7777', '963-481-7945', '111-222-3333'])
highest_sum = 0
highest_phone_number = 0
my_array.each do |phone_number|
sum = phone_number.gsub('-', '').split('').map(&:to_i).reduce(&:+)
if sum > highest_sum
highest_sum = sum
highest_phone_number = phone_number
end
end
puts highest_phone_number
#=> 963-481-7945
You look through each string in the array of phone number strings (my_array) and get the sum of each phone number string. You do this by replacing the dashes ('-') with nothing (''), splitting the phone number string into digits, mapping each digit to an integer, and then summing the digits. Once you get the sum of the phone number, check to see if it is larger than the current highest_sum. If so, set the highest_sum to that sum and the highest_phone_number to the current phone number. After you've looped though all the phone number strings, just output the highest_phone_number.
Here is my solution:
my_array = (['123-456-7777', '963-481-7945', '111-222-3333', '222-111-3333'])
sum_arr = []
sum_hash = {}
my_array.each do |s|
sum = s.gsub("-","").split(//).inject(0) {|z, x| z + x.to_i}
puts sum
sum_hash[sum] = sum_hash[sum] ? sum_hash[sum] << s : [s]
puts sum_hash.inspect
end
puts sum_hash.max_by { |k,v| k }
Part of my solution come from: http://rosettacode.org/wiki/Sum_digits_of_an_integer
Note: The question changed between during I wrote my answer :(

Table displaying the percentages of chosen responses for a vingette (male then female)

People in my study have completed a questionnaire. One of the questions involves the participant reading a scenario/vingette and then they are asked to 'identify the problem' in the scenario. They are then presented with a 9 options (multiple response question).
In my data file men and women are coded numerically. I am currently trying to create a table with the percentage of responses to each of the 9 options for men in one column (adding to 100%), and women in the other (100%).
I know this is probably quite simple, but I've completely forgotten! Any help in exactly how to carry this out in SPSS?
You Can Use the MULT RESPONSE GROUPS Command with the /BASE=RESPONSES option.
*** Create some example fake data.
INPUT PROGRAM.
LOOP #i = 1 TO 15.
LOOP #g = 1 TO 2.
COMPUTE Gender = #g.
DO REPEAT response = r1 TO r9.
COMPUTE response = TRUNC(RV.UNIFORM(0,2)).
END REPEAT.
END CASE.
END LOOP.
END LOOP.
END FILE.
END INPUT PROGRAM.
FORMATS Gender r1 TO r9 (F1.0).
VALUE LABELS Gender
1 'Male'
2 'Female'.
EXECUTE.
And that's how the command looks like with the example data:
MULT RESPONSE GROUPS=$responses (r1 r2 r3 r4 r5 r6 r7 r8 r9 (1))
/VARIABLES=Gender(1 2)
/TABLES=$responses BY Gender
/BASE=RESPONSES
/CELLS=COLUMN.
It sounds like you can get what you want from the CROSSTABS procedure, see example below and image it produces with my default settings.
*Example fake data.
INPUT PROGRAM.
LOOP #i = 1 TO 15.
LOOP #g = 1 TO 2.
COMPUTE Gender = #g.
COMPUTE Response = TRUNC(RV.UNIFORM(1,10)).
END CASE.
END LOOP.
END LOOP.
END FILE.
END INPUT PROGRAM.
FORMATS Gender Response (F1.0).
VALUE LABELS Gender
1 'Male'
2 'Female'.
*Table.
CROSSTABS TABLE Response BY GENDER /CELLS=COL.

Generating a unique and random 6 character long string to represent link in ruby

I am generating a unique and random alphanumeric string segment to represent certain links that will be generated by the users. For doing that I was approaching with "uuid" number to ensure it's uniqueness and randomness, but, as per my requirements the string shouldn't be more than 5 characters long. So I dropped that idea.
Then I decided to generate such a string using random function of ruby and current time stamp.
The code for my random string goes like this:-
temp=DateTime.now
temp=temp + rand(DateTime.now.to_i)
temp= hash.abs.to_s(36)
What I did is that I stored the current DateTime in a temp variable and then I generated a random number passing the current datetime as parameter. Then in the second line actually added current datetime and random number together to make a unique and random string.
Soon I found,while I was testing my application in two different machines and send the request at the same time, it generated the same string(Though it's rare) once after more than 100 trials.
Now I'm thinking that I should add one more parameter like mac address or client ip address before passing to_s(36) on temp variable. But can't figure out how to do it and even then whether it will be unique or nor...
Thanks....
SecureRandom in ruby uses process id (if available) and current time. You can use the urlsafe_base64(n= 16) class method to generate the sequence you need. According to your requirements I think this is your best bet.
Edit: After a bit of testing, I still think that this approach will generate non-unique keys. The way I solved this problem for barcode generation was:
barcode= barcode_sql_id_hash("#{sql_id}#{keyword}")
Here, your keyword can be time + pid.
If you are certain that you will never need more than a given M amount of unique values, and you don't need more than rudimentary protection against guessing the next generated id, you can use a Linear Congruentual Generator to generate your identificators. All you have to do is remember the last id generated, and use that to generate a new one using the following formula:
newid = (A * oldid + B) mod M
If 2³² distinct id values are enough to suit your needs, try:
def generate_id
if #lcg
#lcg = (1664525 * #lcg + 1013904223) % (2**32)
else
#lcg = rand(2**32) # Random seed
end
end
Now just pick a suitable set of characters to represent the id in as little as 6 character. Uppercase and lowercase letters should do the trick, since (26+26)^6 > 2^32:
ENCODE_CHARS = [*?a..?z, *?A..?Z]
def encode(n)
6.times.map { |i|
n, mod = n.divmod(ENCODE_CHARS.size)
ENCODE_CHARS[mod]
}.join
end
Example:
> 10.times { n = generate_id ; puts "%10d = %s" % [n, encode(n)] }
2574974483 = dyhjOg
3636751446 = QxyuDj
368621501 = bBGvYa
1689949688 = yuTgxe
1457610999 = NqzsRd
3936504298 = MPpusk
133820481 = PQLpsa
2956135596 = yvXpOh
3269402651 = VFUhFi
724653758 = knLfVb
Due to the nature of the LCG, the generated id will not repeat until all 2³² values have been used exactly once each.
There is no way you can generate a unique UUID with only five chars, with chars and numbers you have a basic space of around 56 chars, so there is a max of 56^5 combinations , aprox 551 million (Around 2^29).
If with this scheme you were about to generate 10.000 UUIDs (A very low number of UUIDs) you would have a probability of 1/5.000 of generating a collision.
When using crypto, the standard definition of a big enough space to avert collisions is around 2^80.
To put this into perspective, your algorithm would be better off if it generated just a random integer (a 32 bit uint is 2^32, 8 times the size you are proposing) which is clearly a bad idea.

Ruby on Rails random number not working

I've been at this for awhile. I am building a simple lottery website and I am generating random tickets. On my local machine random numbers are generated, however, on the server they are duplicated.
I have tried multiple versions of what I have, but the duplicates is the same.
I need to create a random ticket number per ticket and ensure that it hasn't bee created.
This my like 50th version:
a = Account.find(current_account)
numTics = params[:num_tickets].to_i
t = a.tickets.where(:item_id => item.id).count
total = t + numTics
if total > 5
left = 5 - t
flash[:message] = "The total amount of tickets you can purchase per item is five. You can purchase #{left} more tickets."
redirect_to buy_tickets_path(item.id)
else
i = 1
taken = []
random = Random.new
taken.push(random.rand(100.10000000000000))
code = 0
while i <= numTics do
while(true)
code = random.rand(100.10000000000000)
if !taken.include?(code)
taken.push(code)
if Ticket.exists?(:ticket_number => code) == false
a.tickets.build(
:item_id => item.id,
:ticket_number => code
)
a.save
break
end
code = 0
end
end
i = i + 1
end
session['item_name'] = item.name
price = item.price.to_i * 0.05
total = price * numTics
session['amount_due'] = total.to_i
redirect_to confirmation_path
end
You should be using SecureRandom if possible, not Random. It works the same way but is much more random and doesn't need to be initialized like Random does:
SecureRandom.random_number * 100.1
If you're using Ruby 1.8.7 you can try the ActiveSupport::SecureRandom equivalent.
Also if you're generating lottery tickets, you will want to make sure your generator is cryptographically secure. Generating random numbers alone is probably not sufficient. You will likely want to apply some other function to generate these.
Keep in mind that most actual lotteries do not generate random tickets at the point of purchase, but generate large batches in advance and then issue these to purchasers. This means you are able to preview the tickets and ensure they are sufficiently random.
The problem is not with Ruby's pseudo random number generator but that fact that you are creating generators all the time with Random.new. As explained in this answer, you should not have to call Random.new more than once. Store the result in a global object and you'll be good to go.

Resources