2016-10-25 2 views
0

Мне нужно estimate размер популяции, найдя значение n, которое максимизирует scipy.misc.comb(n, a)/n**b, где a и b являются константами. n, a и b - целые числа.Целочисленная оптимизация/максимизация в numpy

Очевидно, что я мог бы иметь петлю в range(SOME_HUGE_NUMBER), вычислить значение для каждого n и вырваться из цикла, как только достигнет перегиба на кривой. Но я задавался вопросом, был ли элегантный способ сделать это с помощью (например) numpy/scipy или какой-нибудь другой элегантный способ сделать это только в чистом Python (например, как целочисленный эквивалент метода Ньютона?)

+0

Насколько велики вы ожидаете 'n'? Из порядка 181, как в связанном ответе, или более порядка 7,5 миллиардов человек на Земле? – jotasi

+0

Я бы (gut-feel) ожидал n <1000, и, конечно, << 10000, хотя до тех пор, пока я не запустил настоящие данные, я абсолютно не знаю! – TimGJ

+0

Вы можете преобразовать 'comb' в функцию над действиями через гамма-функцию (или аппроксимировать формулой Стирлинга). Затем вы можете использовать метод численного решения, а затем просто проверить, какое ближайшее целое число max. – strubbly

ответ

1

As поскольку ваш номер n достаточно мал (меньше 1500), я предполагаю, что самый быстрый способ сделать это - фактически попробовать все возможные значения. Вы можете сделать это быстро, используя numpy:

import numpy as np 
import scipy.misc as misc 

nMax = 1000 
a = 77 
b = 100 
n = np.arange(1, nMax+1, dtype=np.float64) 
val = misc.comb(n, a)/n**b 
print("Maximized for n={:d}".format(int(n[val.argmax()]+0.5))) 
# Maximized for n=181 

Это не особенно элегантно, но довольно быстро для этого диапазона n. Проблема в том, что для n>1484 числитель уже может быть слишком большим для хранения в float. Затем этот метод завершится неудачей, так как вы столкнетесь с переполнениями. Но это не только проблема numpy.ndarray, не работающая с целыми числами python. Даже с ними, вы не смогли бы вычислить:

misc.comb(10000, 1000, exact=True)/10000**1001 

как вы хотите иметь результат с плавающей точкой в ​​вашем делении двух чисел больше, чем максимум в питоне float может держать (max_10_exp = 1024 на моей системе см. sys.float_info().). Вы также не могли использовать ваш range. Если вы действительно хотите сделать что-то подобное, вам придется проявлять большую осторожность численно.

+0

Спасибо за это. Я бы предположил, что грубая сила будет наиболее эффективным способом ее выполнения (если это не противоречит терминам). Из-за данных, которые я предлагаю, я думаю, что n будет где-то между 100 и 500, хотя пока я не запустил программное обеспечение, я не могу сказать. Но я просто хотел знать, есть ли простой/элегантный способ сделать это. (Любопытно, что вчера вечером я смотрел видео на YouTube, в котором Брайан Керниган говорил о AMPL, что звучит именно так, что может решить эту проблему). – TimGJ

+0

@TimGJ Наверное, есть более элегантное решение. Во всяком случае, вам нужно будет проявлять осторожность в отношении ваших цифр с большими числами. – jotasi

+0

Вы можете рассчитать функцию для больших чисел, избегая сначала вычислять числитель, а затем делить. Если вместо этого вы используете итеративный подход к вычислению 'comb', вы можете разделить на' n' несколько раз, как вы идете, чтобы контролировать размер промежуточного значения. Таким образом, вы можете оценить функцию для сколь угодно больших значений 'n', если общий результат не слишком велик. – strubbly

0

У вас по существу есть приятная плавная функция n, которую вы хотите максимизировать. n требуется, чтобы быть интегралом, но мы можем рассматривать функцию вместо того, чтобы быть функцией от реалов. В этом случае максимизирующее интегральное значение n должно быть близко к (рядом) максимизирующему действительному значению.

Мы можем преобразовать comb в реальную функцию с помощью гамма-функции и использовать численные методы оптимизации, чтобы найти максимум. Другой подход заключается в замене факториалов на приближение Стирлинга. Это дает умеренно сложное, но сговорчивое алгебраическое выражение. Это выражение не трудно отличить и установить на нуль, чтобы найти экстремумы.

Я сделал это и получил

n * (b + (n-a) * log((n-a)/n)) = a * b - a/2 

Это не просто решить алгебраически, но достаточно легко численно (например, с помощью метода Ньютона, как вы предлагаете).

Возможно, я ошибся в алгебре, но я набрал пример a = 77, b = 100 в Wolfram Alpha и получил 180,58, поэтому подход, похоже, работает.

Смежные вопросы