Алгоритмы неформально. Инструкция для начинающих питонистов - [15]

Шрифт
Интервал

def rule1(x,n,upright):

    return((x + ((-1)**upright) * n)%n**2)

В математическом выражении Python интерпретирует True как 1, а False как 0. Если аргумент upright равен False, то наша функция вернет то же значение, что и прежде, так как (–1)0 = 1. Если аргумент upright равен True, то функция будет вычитать n вместо того, чтобы прибавлять, и это позволит нам идти в другом направлении. Проверим, сможет ли функция определить значение справа и выше от 1 в квадрате Ло Шу:

print(rule1(1,3,True))

Функция должна вывести 7, правильное значение из квадрата Ло Шу.

Аналогичная функция создается и для правила 2. Функция для правила 2 получает аргументы x и n, как и функция для правила 1. Но функция для правила 2 по умолчанию изменяет ячейка ниже и правее x. А значит, мы будем прибавлять аргумент upleft, который будет равен True, если вы захотите изменить направление правила. Итоговое правило выглядит так:

def rule2(x,n,upleft):

    return((x + ((-1)**upleft))%n**2)

Функцию можно протестировать на квадрате Ло Шу, хотя в нем всего две пары ячеек, которые не сталкиваются с исключением из правила 2. Для этого исключения можно написать следующую функцию:

def rule3(x,n,upleft):

    return((x + ((-1)**upleft * (-n + 1)))%n**2)

Правило должно соблюдаться только при пересечении антидиагонали магического квадрата. Позднее вы увидите, как определить, пересекается ли антидиагональ.

Итак, теперь мы знаем, как заполнить пять центральных квадратов, и у нас есть правила для заполнения остальных ячеек по известным центральным ячейкам. Можно переходить к заполнению квадрата.


Заполнение остальных ячеек квадрата

Один из способов заполнить остальные ячейки — обойти его случайным образом, используя известные ячейки для заполнения неизвестных. Сначала определим индексы центральной ячейки:

center_i = math.floor(n/2)

center_j = math.floor(n/2)

Затем можно случайным образом выбрать направление для «обхода»:

import random

entry_i = center_i

entry_j = center_j

where_we_can_go = ['up_left','up_right','down_left','down_right']

where_to_go = random.choice(where_we_can_go)

Здесь используется функция Python random.choice(), которая выбирает случайный элемент из списка. Она выбирает элемент из заданного нами множества (where_we_can_go), но делает это случайно (или настолько случайно, насколько это возможно).

После того как направление для перемещения будет выбрано, выполняется правило, соответствующее направлению перемещения. Если выбрано перемещение вниз и налево (down_left) или вверх и направо (up_right), то мы следуем правилу 1, выбирая соответствующие аргументы и индексы:

if(where_to_go == 'up_right'):

    new_entry_i = entry_i - 1

    new_entry_j = entry_j + 1

    square[new_entry_i][new_entry_j] = rule1(square[entry_i][entry_j],n,True)

if(where_to_go == 'down_left'):

    new_entry_i = entry_i + 1

    new_entry_j = entry_j - 1

    square[new_entry_i][new_entry_j] = rule1(square[entry_i][entry_j],n,False)

Аналогичным образом при выборе направления вверх и налево (up_left) или вниз и направо (down_right) используется правило 2:

if(where_to_go == 'up_left'):

    new_entry_i = entry_i - 1

    new_entry_j = entry_j - 1

    square[new_entry_i][new_entry_j] = rule2(square[entry_i][entry_j],n,True)

if(where_to_go == 'down_right'):

    new_entry_i = entry_i + 1

    new_entry_j = entry_j + 1

    square[new_entry_i][new_entry_j] = rule2(square[entry_i][entry_j],n,False)

Код предназначен для перемещения вверх и налево или вниз и направо, но должен выполняться только в том случае, если при этом не пересекается антидиагональ. Необходимо позаботиться о том, чтобы правило 3 выполнялось только при пересечении антидиагонали. Проверить, находится ли значение рядом с антидиагональю, достаточно просто: индексы значений непосредственно над антидиагональю в сумме дают n-2, а индексы значений непосредственно под антидиагональю в сумме дают n. Для этих особых случаев нужно реализовать правило 3:

if(where_to_go == 'up_left' and (entry_i + entry_j) == (n)):

    new_entry_i = entry_i - 1

    new_entry_j = entry_j - 1

    square[new_entry_i][new_entry_j] = rule3(square[entry_i][entry_j],n,True)

if(where_to_go == 'down_right' and (entry_i + entry_j) == (n-2)):

    new_entry_i = entry_i + 1

    new_entry_j = entry_j + 1

    square[new_entry_i][new_entry_j] = rule3(square[entry_i][entry_j],n,False)

Следует помнить, что магический квадрат конечен, поэтому мы не сможем, например, сместиться вверх/налево в верхней строке или в крайнем левом столбце. Создав список направлений, в которых возможно перемещаться от текущей ячейки, можно добавить простую логику, чтобы проверить, происходит ли перемещение только в разрешенных направлениях:

where_we_can_go = []

if(entry_i < (n - 1) and entry_j < (n - 1)):

    where_we_can_go.append('down_right')

if(entry_i < (n - 1) and entry_j > 0):

    where_we_can_go.append('down_left')

if(entry_i > 0 and entry_j < (n - 1)):

    where_we_can_go.append('up_right')

if(entry_i > 0 and entry_j > 0):

    where_we_can_go.append('up_left')

Теперь у нас есть все элементы, которые необходимы для написания кода Python, реализующего алгоритм Курусимы.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.