Вот три способа, которые могут обеспечить псевдослучайность .
Все используют строку
str = "WoLf"
для демонстрации и метод
def change_possible?(str, to_case)
str.count(to_case == :downcase ? 'a-z' : 'A-Z') < str.size
end
, чтобы определить, может ли процедура рандомизации произвести строку, отличную от данной строки, str
.
change_possible?("WoLf", :downcase) #=> true
change_possible?("WoLf", :upcase) #=> true
change_possible?('wolf', :downcase) #=> false
change_possible?('WOLF', :upcase) #=> false
В последних двух подходах используется метод
def char_case(c)
c.downcase == c ? :downcase : :upcase
end
char_case('a') #=> :downcase
char_case('A') #=> :upcase
Измените регистр одного случайно выбранного символа, затем случайным образом определите, следует ли установить регистр каждого из оставшихся символов в указанный регистр 1
def scramble(str, to_case)
return nil unless change_possible?(str, to_case)
i = str.size.times.reject do |i|
char_case(str[i]) == to_case
end.sample
c = str[i].public_send(to_case)
str.gsub(/./) { |s| [s, s.public_send(to_case)].sample }.
tap { |s| s[i] = c }
end
str #=> "WoLf"
scramble(str, :downcase) #=> "Wolf"
scramble(str, :downcase) #=> "woLf"
scramble(str, :downcase) #=> "wolf"
scramble("wolf", :downcase) #=> nil
scramble(str, :upcase) #=> "WOLF"
scramble(str, :upcase) #=> "WOLf"
scramble(str, :upcase) #=> "WOLF"
scramble("WOLF", :upcase) #=> nil
Произвольно определить, следует ли устанавливать регистр каждого символа в указанный регистр, повторяя до тех пор, пока результирующая строка не будет отличаться от данной строки
def scramble(str, to_case)
return nil unless change_possible?(str, to_case)
loop do
s = str.gsub(/./) { |c| [c, c.public_send(to_case)].sample }
break s unless s == str
end
end
str #=> "WoLf"
scramble(str, :downcase) #=> "Wolf"
scramble(str, :downcase) #=> "woLf"
scramble(str, :downcase) #=> "wolf"
scramble("wolf", :downcase) #=> nil
scramble(str, :upcase) #=> "WOLF"
scramble(str, :upcase) #=> "WoLF"
scramble(str, :upcase) #=> "WoLF"
scramble("WOLF", :upcase) #=> nil
Обратите внимание, что c.public_send(to_case)
может равняться c
в [c, c.public_send(to_case)].sample
.
Создайте пробное пространство, затем выберите случайного члена
def scramble(str, to_case)
return nil unless change_possible?(str, to_case)
sample_space(str, to_case).sample
end
def sample_space(str, to_case)
fixed_idx = str.size.times.select do |i|
char_case(str[i]) == to_case
end
len = str.size
(0..2**str.size - 1).map do |n|
len.times.with_object('') do |i,s|
s << (n[i] == 1 ? str[i].upcase : str[i].downcase)
end
end.reject do |s|
s == str || fixed_idx.any? { |i| s[i] != str[i] }
end
end
str #=> "WoLf"
scramble(str, :downcase) #=> "wolf"
scramble(str, :downcase) #=> "woLf"
scramble(str, :downcase) #=> "Wolf"
scramble("wolf", :downcase) #=> nil
scramble(str, :upcase) #=> "WOLF"
scramble(str, :upcase) #=> "WOLF"
scramble(str, :upcase) #=> "WoLF"
scramble("WOLF", :upcase) #=> nil
Для :downcase
пространство выборки было найдено
["wolf", "Wolf", "woLf"]
Для :upcase
,
["WOLf", "WoLF", "WOLF"]
Пространство образца изначально содержит 2**str.size #=> 16
элементов, причем между каждым элементом выборочного пространства и 1 из целых чисел от 0
до 2**str.size - 1
имеется карта 1-1. Отображение соответствует значению целочисленных битов (с добавлением начальных нулей, если необходимо, до str.size
битов), 0
соответствует нижнему регистру, 1
верхнему регистру. Недопустимые строки затем удаляются из пробного пространства.
См. Integer # [] и обратите внимание, что n[i]
может равняться 0
, 1
или nil
, с nil
соответствует ведущему 0
.
Я не претендую на эффективность этого подхода.
1. Это вариант подхода Стефана.