Учитывая отсортированный массив A = [n, n + 1, n + 2, ... n + k] элементов, я пытаюсь подсчитать уникальное количество мультипликативных и аддитивных пар так, чтобы условие xy> = x + y было довольный. Где x и y - это индексы списка, а y> x.
Вот мой минимальный рабочий пример, использующий наивный метод грубой силы:
def minimum_working_example(A):
A.sort()
N = len(A)
mpairs = []
x = 0
while x < N:
for y in range(N):
if x<y and (A[x]*A[y])>=(A[x]+A[y]):
mpairs.append([A[x], A[y]])
else:
continue
x+=1
return len(mpairs)
A = [1,2,3,4,5]
print(minimum_working_example(A))
#Output = 6, Unique pairs that satisfy xy >= x+y: (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)
Однако этот подход имеет экспоненциальную временную сложность для больших списков.
Какие существуют алгоритмы сортировки или поиска, которые позволят мне реализовать более эффективное решение?
Используйте математику: x * y> = x + y, как только x или y больше 1, поэтому, если n> = 2, это k * (k + 1) / 2, а если n == 1, это k * (k- 1) / 2
Разве x и y не были бы одними и теми же индексами в вашем примере ..?
Алгоритм не экспоненциальный. Он квадратичный, и можно создавать списки, в которых результаты также масштабируются квадратично, следовательно, с точки зрения временной сложности мы мало что можем сделать.
Спасибо @Julien. Я не обращал внимания.
@Julien Где вы получаете k (k-1) / 2 и k (k + 1) / 2 для n == 1 и n> = 2? Я попытался ввести k = 1,2,3,4 в k (k-1) / 2 и k (k + 1) / 2, и получил 0,1,3,6 ... и 1,3,6 , 10 ...? Хотите узнать, что пытается сделать ваш подход? Вы упомянули, что это дает временную сложность O (1)
@bwrr см. мой ответ (обратите внимание, что в моем комментарии была небольшая ошибка, о которой никто не упоминал, несмотря на 5 голосов :)
Этот вопрос имеет математическое решение в закрытой форме, но если вы предпочитаете реализовать его на языке программирования, вам просто нужно найти все уникальные пары чисел из вашего списка и подсчитать число, удовлетворяющее вашим требованиям. itertools.combinations
- ваш друг здесь:
import itertools
A = [1,2,3,4,5]
pairs = []
for x, y in itertools.combinations(A, 2):
if x*y >= x + y:
pairs.append((x,y))
Вывод
[(2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
Прости. Я ушел.
В каждой из этих пар произведение меньше суммы: 1 * 2 <1 + 2, 1 * 3 <1 + 3 и т. д.
Этот подход кажется еще медленнее. Потребовалось 55 секунд, чтобы запустить список из 10000 случайных целых чисел.
Базовая алгебра ... решите одну переменную с точки зрения другой:
xy >= x + y
xy - y >= x
y(x-1) >= x
Теперь, если все ваши элементы являются положительными целыми числами, вы получите
if x == 1, no solution
if x == 2, y >= 2
else x > 2
y >= x/(x-1)
В этом последнем случае x / (x-1) представляет собой дробь от 1 до 2; снова,
y >= 2
Решает неравенство.
Это дает вам тривиально доступное решение за время О (1); если вам нужны сами пары, вы ограничены печатью, которая составляет время О (п ^ 2).
Зачем останавливаться на достигнутом? В O (1) тоже есть "тривиальное" решение, о котором я упоминал в своем комментарии ...
@Julien: Я не согласен: спецификации программы включают возврат списка подходящих пар. Это то, что побуждает ваше и мое решение перейти от О (1) к О (п ^ 2).
Ну нет. OP запрашивает количество пар, а не сами пары. Код OP вычисляет список только для того, чтобы взять его длину в конце ... Так что нет необходимости в O (n ^ 2) (который, кстати, должен быть O (k ^ 2), учитывая их обозначения ...)
Ах ... Я интерпретировал выходную строку как требующую как счетчика, так и самих пар. Я понимаю вашу точку зрения.
Если вам нужно более математическое решение, учтите, что xy > x+y
не имеет решений для y=1
. В противном случае вы можете алгебраически обработать это до x > y/(y-1)
. Теперь, если у нас есть два последовательных целых числа положительный и разделив большее на меньшее, мы либо получим ровно 2 (если y = 2), либо получим некоторую дробь от 1 до 2, исключая. Обратите внимание, что x должен быть больше, чем это отношение y / (y-1), но также должно быть меньше y. Если y = 2, то единственное возможное значение x в нашем списке положительных целых чисел должно быть 1, и в этом случае совпадений нет, потому что 1 не больше 2/1. Таким образом, все это упрощается до «Для каждого числа y в нашем списке посчитайте все значения x, которые находятся в диапазоне [2, y)». Если вы сделаете математику, это должно привести к сложению 1 + 2 + 3 + ... + k, что является просто k(k+1)/2
. Опять же, мы предполагаем, что n и k - положительные целые числа; вы можете вывести немного более сложную формулу, если учесть случаи для n <= 0.
Но если предположить, что вы ДЕЙСТВИТЕЛЬНО хотите придерживаться подхода грубой силы, а не прибегать к математическим рассуждениям, чтобы найти другой подход: я опробовал несколько вариантов, и вот более быстрое решение, основанное на следующем.
itertools
для генерации уникальных пар всех чисел в вашем списке A
pairs
, а не о самих парах чисел, тогда вы можете просто подсчитывать пары по пути, а не сохранять их. В противном случае у вас может закончиться нехватка памяти при высоких значениях N.Итак, вот что у меня есть:
def example4(A):
mpair_count = 0
for pair in itertools.combinations(A, 2):
if pair[0]*(pair[1]-1) - pair[1] > 0:
mpair_count += 1
return mpair_count
Вот все рассчитано:
from timeit import default_timer as timer
import itertools
def minimum_working_example(A):
A.sort()
N = len(A)
mpairs = []
x = 0
while x < N:
for y in range(N):
if x<y and (A[x]*A[y])>=(A[x]+A[y]):
mpairs.append([A[x], A[y]])
else:
continue
x+=1
return len(mpairs)
# Cutting down the range
def example2(A):
N = len(A)
mpairs = []
x = 0
while x < N:
for y in range(x+1,N):
if (A[x]*A[y])>=(A[x]+A[y]):
mpairs.append([A[x], A[y]])
x += 1
return len(mpairs)
# Using itertools
def example3(A):
mpair_count = 0
for pair in itertools.combinations(A, 2):
if pair[0]*pair[1] > sum(pair):
mpair_count += 1
return mpair_count
# Using itertools and the different comparison
def example4(A):
mpair_count = 0
for pair in itertools.combinations(A, 2):
if pair[0]*(pair[1]-1) - pair[1] > 0:
mpair_count += 1
return mpair_count
# Same as #4, but slightly different
def example5(A):
mpair_count = 0
for pair in itertools.combinations(A, 2):
if pair[0]*(pair[1]-1) > pair[1]:
mpair_count += 1
return mpair_count
A = range(1,5000)
start = timer()
print(minimum_working_example(A))
end = timer()
print(end - start)
start = timer()
print(example2(A))
end = timer()
print(end - start)
start = timer()
print(example3(A))
end = timer()
print(end - start)
start = timer()
print(example4(A))
end = timer()
print(end - start)
start = timer()
print(example5(A))
end = timer()
print(end - start)
Результат:
12487503
8.29403018155
12487503
7.81883932384
12487503
3.39669140954
12487503
2.79594281764
12487503
2.92911447083
Итак, используя тот факт, что x*y >= x+y
, если оба (ошибка в моем исходном комментарии), x
и y
- это >=2
(подробности см. В ответе @ Prune), тогда вы также можете удалить 0
и 1
из своего списка, если они появятся, потому что они не будут составить любую подходящую пару.
Итак, теперь, предполагая все числа или >=2
и у вас есть k
из них (например, замените k
на k-1
в следующей операции, если у вас есть n=1
), все возможные пары будут удовлетворять вашему условию. А количество пар среди элементов k
- это хорошо известная формула k*(k-1)/2
(погуглите, если не знаете). Время для вычисления этого числа по существу одинаково (одно умножение, одно деление) независимо от того, какое у вас значение k
(если только вы не начнете переходить к сумасшедшим большим числам), поэтому сложность равна O (1).
Это предполагает, что ваши целые числа положительны, в противном случае формула будет немного сложнее, но все же возможно в качестве решения в закрытой форме.
Хотя вы реализуете это на языке программирования, это в основном кажется математическим вопросом (по крайней мере, для меня). Я бы рассмотрел 2 + 2 против 2 * 2, а затем подумал бы, что произойдет, если один вход меньше 2, и что произойдет, когда оба будут больше 2.