问题
I have a function that reads a file byte by byte and converts it to a floating point array. It also returns the number of elements in said array. Now I want to reshape the array into a 2D array with the shape being as close to a square as possible.
As an example let's look at the number 800:
sqrt(800) = 28.427...
Now by I can figure out by trial and error that 25*32
would be the solution I am looking for.
I do this by decrementing the sqrt
(rounded to nearest integer) if the result of multiplying the integers is to high, or incrementing them if the result is too low.
I know about algorithms that do this for primes, but this is not a requirement for me. My problem is that even the brute force method I implemented will sometimes get stuck and never finish (which is the reason for my arbitrary limit of iterations):
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)
using this script above the output will get stuck in a loop printing
[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
But I wonder if there are more efficient solutions for this? Certainly I can't be the first to want to do something like this.
回答1:
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
try it with:
for x in xrange(10, 20):
print factor_int(x)
回答2:
Interesting question, here's a possible solution to your problem:
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)
The key of this method is finding the minimum distance to the cartesian point of [math.sqrt(N), math.sqrt(N)] which meets the requirements N=a*b, a&b integers.
回答3:
I think the modulus operator is a good fit for this problem:
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
回答4:
Here's a direct method that finds the smallest, closest integers a
, b
, such that a * b >= n
, and a <= b
, where n
is any number:
def factor_int(n):
a = math.floor(math.sqrt(n))
b = math.ceil(n/float(a))
return a, b
try it with:
for x in xrange(10, 20):
print factor_int(x)
回答5:
Here's a shorter code based on the currently accepted answer that is shorter and takes about 25%-75% less time to run than their code (from basic timeit tests):
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
And here's a small and messy test I made to test the efficiency of the method:
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'
来源:https://stackoverflow.com/questions/39248245/factor-an-integer-to-something-as-close-to-a-square-as-possible