Как сравнить целые индексы в массивах при наличии повторяющихся значений?
Во-первых, некоторый необходимый фон. Я пытаюсь сделать числовую версию игры Mastermind как способ обучения коду в Ruby. Мой код в основном работает следующим образом:
- Компьютер генерирует массив (
@computer_sequence
) из 4 случайных чисел из 1-5
- Пользователь вводит 4-значную последовательность, которая завершается в массиве с именем
@user_array
.
- Метод, называемый
compare
, выполняет итерацию через @user_array
, сравнивая значение и индекс каждого числа с параметрами в @computer_sequence.
. Затем программа сообщает пользователю, сколько их номеров имеет правильное значение и правильное положение, или сколько чисел имеет только правильное значение.
Проблема: Если в массиве имеется несколько экземпляров числа, они получают одинаковый индекс, правильно? Например, если у меня есть массив [1, 3, 3, 4]
, номер три имеет индекс 1, хотя есть два 3s. Тем не менее, для работы этой программы каждый номер должен иметь уникальную позицию (это индекс, даже слово, которое я хочу здесь?) В массиве, даже если число происходит несколько раз. Это имеет смысл?
Кроме того, здесь код для метода compare
:
def compare
value_only = 0
value_and_place = 0
puts "The computer values are: #{@computer_sequence}"
puts "The user values are: #{@user_array}"
@user_array.each do |candidate|
@computer_sequence.each do |computer_number|
if candidate == computer_number && @user_array.index(candidate) == @computer_sequence.index(computer_number)
value_and_place +=1
elsif candidate == computer_number && @user_array.index(candidate) != @computer_sequence.index(computer_number)
value_only +=1
end
end
end
Ответы
Ответ 1
предполагать
n = 4
computer = Array.new(n) { [1,2,3,4,5].sample }
#=> [3, 2, 3, 3]
user_digits = [2, 4, 2, 3]
Сначала вычислите пары элементов в одном и том же индексе computer
и user_digits
.
pairs = computer.zip(user_digits)
#=> [[3, 2], [2, 4], [3, 2], [3, 3]]
Вычислить количество значений, совпадающих в одном и том же положении
pairs.count { |c,u| c==u }
#=> 1
Вычислить количество значений, совпадающих на разных позициях
Сначала удалите совпадения в тех же положениях computer
и user_digits
.
comp, users = pairs.reject { |c,u| c==u }.transpose
#=> [[3, 2, 3], [2, 4, 2]]
имея в виду
comp #=> [3, 2, 3]
users #=> [2, 4, 2]
Теперь пройдите через users
удалив первый соответствующий элемент в comp
(если он есть).
users.each do |n|
i = comp.index(n)
comp.delete_at(i) if i
end
А сейчас:
comp #=> [3,3]
что количество элементов, которые соответствуют различным позициям:
users.size-comp.size
#=> 1
Обратите внимание, что мы могли бы альтернативно вычислить количество значений, которые совпадают в том же положении, что и
n - users.size
Для n
равного 4
это не дает сколько-нибудь существенной экономии времени, но если бы у нас была проблема с той же структурой и n
были большими.
Альтернативный расчет
После вычисления
comp, users = pairs.reject { |c,u| c==u }.transpose
мы могли бы написать
users.size - comp.difference(users).size
#=> 1
где Array#difference
, как я определил ее в моем ответе здесь.
Вот
comp.difference(users)
#=> [3,3]
Ответ 2
Нет, равные элементы в массиве не имеют одинакового индекса. Возможно, вы думаете, что, поскольку Array#index
возвращает только индекс первого элемента, равный его аргументу. Но есть много способов увидеть, что другие равные элементы имеют свои собственные индексы. Например,
a = [1, 3, 3, 4]
a[1] == 3 # true
a[2] == 3 # also true
Помимо этой проблемы, ваш алгоритм не совсем соответствует правилам Mastermind. Если в компьютерной последовательности есть три, и игрок угадывает две тройки, как в разных позициях, чем три в компьютерной последовательности, игроку следует сказать, что только один элемент их последовательности соответствует компьютерной последовательности по значению, но не положению.
Учитывая вышеизложенное, плюс, что я думаю, что было бы яснее рассчитать два числа отдельно, я бы сделал это следующим образом:
value_and_place = 4.times { |i| @user_array[i] == @computer_sequence[i] }
value_only = (@user_array & @computer_sequence).length - value_and_place
Это менее эффективно, чем подход, который вы используете, но эффективность процессора не важна для 4-элементных массивов.
Ответ 3
Вы можете передать значение index
в ваш цикл для каждого candidate
с помощью метода each_with_index
. Итак, когда первые 3 пройдены, index
будет 1
, а когда второе 3 будет передано, index
будет 2
.
Проблема с использованием .index(candidate)
заключается в том, что он возвращает первый индекс.
Попробуйте следующее:
@user_array.each_with_index do |candidate, index|
@computer_sequence.each do |computer_number|
if candidate == computer_number && candidate == @computer_sequence[index]
value_and_place +=1
elsif candidate == computer_number && candidate != @computer_sequence[index]
value_only +=1
end
end
end
Ответ 4
Почему бы не сохранить значения компьютера как хэш с цифрами в виде ключей и индексы как значения?
#example hash equivalent to [1, 3, 3, 4]
@computer_sequence = {1=>[0], 3 => [1,2], 4 => [3]}
@user_hash.each_with_index do |v, i|
if @computer_sequence.values.flatten.include?(v)
if @computer_sequence[v].include?(i)
value_and_place += 1
else
value_only += 1
end
end
end