Фактор целое число к чему-то как можно ближе к квадрату
У меня есть функция, которая читает файл побайтно и преобразует его в массив с плавающей запятой. Он также возвращает количество элементов в указанном массиве. Теперь я хочу преобразовать массив в 2D-массив так, чтобы форма была как можно ближе к квадрату.
В качестве примера давайте посмотрим на число 800:
sqrt(800) = 28.427...
Теперь я могу определить методом проб и ошибок, что 25*32
будет решением, которое я ищу. Я делаю это, уменьшая sqrt
(округляется до ближайшего целого), если результат умножения целых чисел слишком велик, или увеличивать их, если результат слишком мал.
Я знаю об алгоритмах, которые делают это для простых чисел, но это не требование для меня. Моя проблема в том, что даже метод грубой силы, который я реализовал, иногда застревает и никогда не заканчивается (что является причиной моего произвольного ограничения итераций):
import math
def factor_int(n):
nsqrt = math.ceil(math.sqrt(n))
factors = [nsqrt, nsqrt]
cd = 0
result = factors[0] * factors[1]
ii = 0
while (result != n or ii > 10000):
if(result > n):
factors[cd] -= 1
else:
factors[cd] += 1
result = factors[0] * factors[1]
print factors, result
cd = 1 - cd
ii += 1
return "resulting factors: {0}".format(factors)
input = 80000
factors = factor_int(input)
используя этот скрипт выше, вывод застрянет в циклической печати
[273.0, 292.0] 79716.0
[273.0, 293.0] 79989.0
[274.0, 293.0] 80282.0
[274.0, 292.0] 80008.0
[273.0, 292.0] 79716.0
[273.0, 293.0] 79989.0
[274.0, 293.0] 80282.0
[274.0, 292.0] 80008.0
[273.0, 292.0] 79716.0
[273.0, 293.0] 79989.0
[274.0, 293.0] 80282.0
[274.0, 292.0] 80008.0
[273.0, 292.0] 79716.0
[273.0, 293.0] 79989.0
[274.0, 293.0] 80282.0
[274.0, 292.0] 80008.0
[273.0, 292.0] 79716.0
[273.0, 293.0] 79989.0
[274.0, 293.0] 80282.0
Но мне интересно, есть ли более эффективные решения для этого? Конечно, я не могу быть первым, кто хочет сделать что-то подобное.
7 ответов
def factor_int(n):
nsqrt = math.ceil(math.sqrt(n))
solution = False
val = nsqrt
while not solution:
val2 = int(n/val)
if val2 * val == float(n):
solution = True
else:
val-=1
return val, val2, n
попробуйте это с:
for x in xrange(10, 20):
print factor_int(x)
Я думаю, что оператор модуля хорошо подходит для этой проблемы:
import math
def factint(n):
pos_n = abs(n)
max_candidate = int(math.sqrt(pos_n))
for candidate in xrange(max_candidate, 0, -1):
if pos_n % candidate == 0:
break
return candidate, n / candidate
Вот более короткий код, основанный на принятом в настоящее время ответе, который короче и занимает на 25%-75% меньше времени, чем их код (из базовых тестов timeit):
from math import sqrt, ceil
def factor_int_2(n):
val = ceil(sqrt(n))
while True:
if not n%val:
val2 = n//val
break
val -= 1
return val, val2
А вот небольшой и беспорядочный тест, который я провел для проверки эффективности метода:
print("Method 2 is shorter and about {}% quicker".format(100*(1 - timeit(lambda: factor_int_2(12345))/timeit(lambda: factor_int(12345)))))
#returns 'Method 2 is shorter and about 75.03810670186826% quicker'
Вот прямой метод, который находит самые маленькие и ближайшие целые числа a
, b
, так что a * b >= n
, а также a <= b
, где n
любое число:
def factor_int(n):
a = math.floor(math.sqrt(n))
b = math.ceil(n/float(a))
return a, b
попробуйте:
for x in xrange(10, 20):
print factor_int(x)
Интересный вопрос, вот возможное решение вашей проблемы:
import math
def min_dist(a, b):
dist = []
for Pa in a:
for Pb in b:
d = math.sqrt(
math.pow(Pa[0] - Pb[0], 2) + math.pow(Pa[1] - Pb[1], 2))
dist.append([d, Pa])
return sorted(dist, key=lambda x: x[0])
def get_factors(N):
if N < 1:
return N
N2 = N / 2
NN = math.sqrt(N)
result = []
for a in range(1, N2 + 1):
for b in range(1, N2 + 1):
if N == (a * b):
result.append([a, b])
result = min_dist(result, [[NN, NN]])
if result:
return result[0][1]
else:
return [N, 1]
for i in range(801):
print i, get_factors(i)
Ключом этого метода является нахождение минимального расстояния до декартовой точки [math.sqrt(N), math.sqrt(N)], которое удовлетворяет требованиям целых чисел N=a*b, a&b.
Oneline код для одного номера:
import numpy as np
n = 800
(np.ceil(np.sqrt(n)), np.ceil(n/np.ceil(np.sqrt(n))))
>>> (29.0, 28.0)
Не уверен, что это то, о чем вы просили, но это намного ближе к квадрату, чем (25,32), как вы упомянули в описании. Надеюсь, поможет!
Это эквивалентно нахождению факторов(b,c)
дляa=b*c
такой, что меньший фактор,b
, — наибольшее число, не превышающееsqrt(a)
. Это можно решить:
import math
def closest_factors(a):
for b in range(int(math.sqrt(a)), 0, -1):
if a%b == 0:
c = a // b
return (b,c)
print(closest_factors(800))
возвращает (25,32).