Также считается, что на основе этой гипотезы могут быть доказаны другие известные теоремы, так что ее доказательство было бы важным для математики в целом.
Разумеется, мы здесь не претендуем на доказательство гипотезы, но можем проверить некоторые значения на языке Python.
Для начала, возьмем функцию нахождения простых множителей числа N.
def prime_factors(n):
factors = []
while n % 2 == 0:
factors.append(int(2))
n = n / 2
for i in range(3, int(math.sqrt(n)) + 1, 2):
# while i divides n , print i ad divide n
while n % i == 0:
factors.append(int(i))
n = n / i
if n > 2:
factors.append(int(n))
return factors
Как можно видеть, функция последовательно делит числа, и если остаток от деления равен 0, результат заносится в массив. Отдельно проверяются два частных случая - если число четное (делится на 2) и если число простое (ни на что не делится кроме себя). В результате функция возвращает массив чисел, напримем для 8 это будет [2,2,2] (2*2*2 = 8).
Следующий шаг - напишем функцию вычисления радикала. Для этого мы воспользуемся множеством (set), которое хранит неповторяющиеся элементы массива prime_factors.
def rad(n):
result = 1
for num in set(prime_factors(n)):
result *= num
return result
Третья функция - проверка на то, что 3 числа являются взаимно простыми, и у них нет общих делителей.
def not_mutual_primes(a,b,c):
fa, fb, fc = set(prime_factors(a)), set(prime_factors(b)), set(prime_factors(c))
return len(fa.intersection(fb)) == 0 and len(fa.intersection(fc)) == 0 and
len(fb.intersection(fc)) == 0
Здесь мы пользуемся функцией нахождения пересечения множеств языка Python.
Написанных 3х функций достаточно, чтобы написать функцию вывода троек чисел A,B,C:
def calculate(N):
S = 1.2
cnt = 0
for a in range(1, N):
for b in range(1, N):
if b < a: continue
c = a+b
if not_mutual_primes(a, b, c):
if c > (rad(a*b*c))**S:
print("{} + {} = {}".format(a, b, c))
cnt += 1
print("N: {}, CNT: {}".format(N, cnt))
return cnt
Как написано в условии задачи, количество таких троек должно быть ограниченно. Мы можем вывести график количества троек от максимального значения N, тогда будет примерно видно, как быстро он растет.
import matplotlib.pyplot as plt
x_values = range(1,2000,25)
y_values = list(map(calculate, x_values))
plt.plot(x_values, y_values, 'ro', label='count(N), S=1.2')
plt.legend()
plt.show()
Как можно видеть, количество троек реально мало - в диапазоне до 2000 их всего 10:
1 + 8 = 9
1 + 80 = 81
1 + 242 = 243
1 + 288 = 289
1 + 512 = 513
3 + 125 = 128
5 + 1024 = 1029
13 + 243 = 256
49 + 576 = 625
81 + 1250 = 1331
На графике это хорошо видно, более того, видно что рост графика замедляется, и число троек скорее всего, действительно ограничено для любого диапазона чисел.
Приложение 1 - Вычисления с помощью видеокарты
Еще 20 лет назад, во времена процессоров 80386, пользователям приходилось покупать математический сопроцессор, позволяющий быстрее выполнять вычисления с плавающей точкой. Сейчас такой сопроцессор покупать уже не надо - благодаря прогрессу в игровой индустрии, даже встроенная видеокарта компьютера имеет весьма неплохую вычислительную мощность. Например, даже бюджетный видеочип Intel Graphics 4600 имеет 20 вычислительных блоков, что превышает количество ядер “основного” процессора. Разумеется, каждое ядро GPU по отдельности слабее CPU, но здесь как раз тот случай, когда количество дает преимущество над качеством. Вычисления с помощью GPU сейчас очень популярны - от майнинга биткоинов до научных расчетов, диапазон ценовых решений также различен, от “бесплатной” встроенной видеокарты до NVIDIA Tesla ценой более 100тыс рублей. Поэтому интересно посмотреть, как же это работает.
Есть две основные библиотеки для GPU-расчетов - NVidia CUDA и OpenCL. Первая обладает большими возможностями, однако работает только с картами NVIDIA. Библиотека OpenCL работает с гораздо большим числом графических карт, поэтому мы рассмотрим именно ее.
Основной принцип GPU-расчетов - параллельность вычислений. Данные, хранящиеся в “глобальной памяти” (global & constant memory) устройства, обрабатываются модулями (каждый модуль называется “ядром”, или “kernel”), каждый из которых работает параллельно с другими. Модуль имеет и свою собственную память для промежуточных данных (private memory). Так это выглядит в виде блок-схемы: