Я ищу самый быстрый способ определить, является ли long
значение идеальным квадратом (то есть его квадратный корень является другим целым числом):
- Я сделал это простым способом, используя встроенный
Math.sqrt()
функцию, но мне интересно, есть ли способ сделать это быстрее, ограничив себя только целочисленной областью. - Ведение справочной таблицы нецелесообразно (поскольку имеется около 2 31,5 целых чисел, площадь которых меньше 2 63 ).
Вот очень простой и понятный способ сделать это сейчас:
public final static boolean isPerfectSquare(long n)
{
if (n < 0)
return false;
long tst = (long)(Math.sqrt(n) + 0.5);
return tst*tst == n;
}
Примечание: я использую эту функцию во многих Project Euler задачах . Так что больше никому не придется поддерживать этот код. И этот вид микрооптимизации может реально изменить ситуацию, так как одна из задач состоит в том, чтобы выполнить каждый алгоритм менее чем за минуту, и в некоторых задачах эту функцию нужно будет вызывать миллионы раз.
Я пробовал разные решения проблемы:
- После исчерпывающего тестирования я обнаружил, что добавление
0.5
к результату Math.sqrt () необязательно, по крайней мере, на моей машине. - Быстрый обратный квадратный корень был быстрее, но он дал неправильные результаты при п> = 410881. Однако, как это было предложено BobbyShaftoe , мы можем использовать FISR хак для п <410881.
- Метод Ньютона был немного медленнее, чем
Math.sqrt()
. Вероятно, это связано с тем, чтоMath.sqrt()
используется метод, подобный методу Ньютона, но реализованный в аппаратном обеспечении, поэтому он намного быстрее, чем в Java. Кроме того, метод Ньютона все еще требовал использования двойных чисел. - Модифицированный метод Ньютона, который использовал несколько приемов так, чтобы была задействована только целочисленная математика, требовал некоторых хаков, чтобы избежать переполнения (я хочу, чтобы эта функция работала со всеми положительными 64-битными целыми числами со знаком), и это было все еще медленнее, чем
Math.sqrt()
. - Бинарная отбивная была еще медленнее. Это имеет смысл, потому что двоичной отбивке в среднем потребуется 16 проходов, чтобы найти квадратный корень 64-битного числа.
- Согласно тестам Джона, использование
or
операторов в C ++ быстрее, чем использование aswitch
, но в Java и C #, похоже, нет разницы междуor
иswitch
. - Я также попытался создать таблицу поиска (как частный статический массив из 64 логических значений). Тогда вместо того, чтобы менять или
or
утверждать, я бы просто сказалif(lookup[(int)(n&0x3F)]) { test } else return false;
. К моему удивлению, это было (немного) медленнее. Это потому, что границы массивов проверяются в Java .
((1<<(n&15))|65004) != 0
, вместо трех отдельных проверок.Ответы:
Я нашел метод, который работает примерно на 35% быстрее, чем ваш код 6bit + Carmack + sqrt, по крайней мере, с моим процессором (x86) и языком программирования (C / C ++). Ваши результаты могут отличаться, особенно потому, что я не знаю, как будет действовать фактор Java.
Мой подход тройной:
int64 x
.)z = r - x * x
и задаю t как наибольшую степень деления z на 2 с небольшим фокусом. Это позволяет мне пропустить t значений, которые не повлияли бы на значение r в любом случае. Предварительно вычисленное начальное значение в моем случае выбирает «наименьший положительный» квадратный корень по модулю 8192.Даже если этот код не работает для вас быстрее, я надеюсь, вам понравятся некоторые идеи, которые он содержит. Далее следует полный, проверенный код, включая предварительно вычисленные таблицы.
источник
9 < 0 => false
,9&2 => 0
,9&7 == 5 => false
,9&11 == 8 => false
.Я довольно поздно на вечеринку, но я надеюсь дать лучший ответ; короче и (при условии моего тест верен) также намного быстрее .
Первый тест ловит большинство не квадратов быстро. Он использует таблицу из 64 элементов, упакованную в long, поэтому нет затрат на доступ к массиву (проверка косвенности и границ). Для равномерно случайной
long
, здесь есть вероятность окончания 81,25%.Второй тест ловит все числа, имеющие нечетное число двойок в их факторизации. Этот метод
Long.numberOfTrailingZeros
очень быстрый, поскольку он превращает JIT-ed в одну инструкцию i86.После отбрасывания конечных нулей третий тест обрабатывает числа, заканчивающиеся на 011, 101 или 111 в двоичном виде, которые не являются идеальными квадратами. Он также заботится об отрицательных числах и обрабатывает 0.
Финальный тест возвращается к
double
арифметике. Так какdouble
имеет только 53 бита мантиссы, преобразование изlong
вdouble
включает в себя округление для больших значений. Тем не менее, тест является правильным (если доказательство не ).Попытка включить идею mod255 не удалась.
источник
goodMask
тест это делает, но он делает это прежде , чем сдвиг вправо. Так что вам придется повторить это, но так проще и AFAIK чуть-чуть быстрее и одинаково хорошо.if ((x & (7 | Integer.MIN_VALUE)) != 1) return x == 0;
.Вам нужно будет сделать несколько тестов. Лучший алгоритм будет зависеть от распределения ваших входных данных.
Ваш алгоритм может быть почти оптимальным, но вы можете сделать быструю проверку, чтобы исключить некоторые возможности, прежде чем вызывать подпрограмму квадратного корня. Например, посмотрите на последнюю цифру вашего числа в шестнадцатеричном виде, выполнив побитовое «и». Совершенные квадраты могут заканчиваться только 0, 1, 4 или 9 в основании 16, так что для 75% ваших входных данных (при условии, что они распределены равномерно) вы можете избежать вызова квадратного корня в обмен на какое-то очень быстрое переключение битов.
Кип протестировал следующий код, реализующий шестнадцатеричный трюк. При тестировании чисел от 1 до 100 000 000 этот код выполнялся в два раза быстрее оригинала.
Когда я тестировал аналогичный код в C ++, он на самом деле работал медленнее, чем оригинал. Однако, когда я исключил оператор switch, шестнадцатеричный трюк снова сделал код в два раза быстрее.
Исключение оператора switch мало повлияло на код C #.
источник
Я думал об ужасных временах, которые я провел в курсе численного анализа.
И потом я помню, что эта функция кружила по сети из исходного кода Quake:
Который в основном вычисляет квадратный корень, используя функцию приближения Ньютона (не могу вспомнить точное имя).
Это должно быть удобно и даже быстрее, это из одной из феноменальных игр id!
Он написан на C ++, но не должно быть слишком сложно повторно использовать ту же технику в Java, как только вы получите идею:
Первоначально я нашел его по адресу: http://www.codemaestro.com/reviews/9
Метод Ньютона объяснен в Википедии: http://en.wikipedia.org/wiki/Newton%27s_method
Вы можете перейти по ссылке для более подробного объяснения того, как это работает, но если вам все равно, то это примерно то, что я помню из чтения блога и прохождения курса численного анализа:
* (long*) &y
основном это функция быстрого преобразования в long, поэтому целые операции могут применяться к необработанным байтам.0x5f3759df - (i >> 1);
линия представляет собой предварительно рассчитанное значение семян для функции аппроксимации.* (float*) &i
преобразует значение обратно с плавающей точкой.y = y * ( threehalfs - ( x2 * y * y ) )
линия Bascially итерации значения над функцией снова.Функция приближения дает более точные значения, чем больше вы повторяете функцию по результату. В случае с Quake, одна итерация «достаточно хороша», но если бы она была не для вас ... тогда вы могли бы добавить столько итераций, сколько вам нужно.
Это должно быть быстрее, потому что это уменьшает количество операций деления, выполняемых в простом квадратном корне, до простого деления на 2 (фактически
* 0.5F
операция умножения) и заменяет его на несколько фиксированных чисел операций умножения.источник
Я не уверен, будет ли это быстрее или даже точнее, но вы можете использовать алгоритм магического квадратного корня Джона Кармака , чтобы быстрее решить квадратный корень. Вероятно, вы могли бы легко проверить это для всех возможных 32-битных целых чисел и убедиться, что вы действительно получили правильные результаты, так как это всего лишь приближение. Тем не менее, теперь, когда я думаю об этом, использование двойных чисел также приближенно, так что я не уверен, как это вступит в игру.
источник
Если вы выполните двоичную отбивку, чтобы попытаться найти «правильный» квадратный корень, вы можете довольно легко определить, достаточно ли близкое вам значение, чтобы сказать:
Итак, рассчитав
n^2
, варианты:n^2 = target
: сделано, верни истинуn^2 + 2n + 1 > target > n^2
: ты близок, но не идеален: верни ложьn^2 - 2n + 1 < target < n^2
: то же самоеtarget < n^2 - 2n + 1
: бинарная отбивная на нижнемn
target > n^2 + 2n + 1
: бинарная отбивная на высшемn
(Извините, это использует
n
как ваше текущее предположение, так иtarget
для параметра. Приносим извинения за путаницу!)Я не знаю, будет ли это быстрее или нет, но стоит попробовать.
РЕДАКТИРОВАТЬ: бинарная отбивная также не должна принимать весь диапазон целых чисел,
(2^x)^2 = 2^(2x)
поэтому, как только вы найдете верхний установленный бит в вашей цели (что можно сделать с помощью хитрого трюка; я точно забыл, как) Вы можете быстро получить диапазон возможных ответов. Имейте в виду, что наивный бинарная отбивная все еще займет всего 31 или 32 итерации.источник
Я провел собственный анализ нескольких алгоритмов в этой теме и получил новые результаты. Вы можете увидеть эти старые результаты в истории редактирования этого ответа, но они не точные, так как я допустил ошибку и потратил время на анализ нескольких алгоритмов, которые не являются близкими. Однако, извлекая уроки из нескольких разных ответов, у меня теперь есть два алгоритма, которые сокрушают «победителя» этой темы. Вот основная вещь, которую я делаю иначе, чем все остальные:
Однако эта простая строка, которая в большинстве случаев добавляет одну или две очень быстрые инструкции, значительно упрощает
switch-case
оператор в один оператор if. Тем не менее, это может добавить к времени выполнения, если многие из протестированных чисел имеют значительную степень двух факторов.Алгоритмы ниже следующие:
Вот пример времени выполнения, если числа генерируются с использованием
Math.abs(java.util.Random.nextLong())
А вот пример времени выполнения, если он запускается только для первого миллиона длинных:
Как видите,
DurronTwo
лучше справляется с большими входами, потому что он очень часто использует магический трюк, но затупляется по сравнению с первым алгоритмом иMath.sqrt
потому, что числа намного меньше. Между тем, более простойDurron
выигрывает, потому что ему никогда не приходится делить на 4 много много раз числа первого миллиона.Вот
Durron
:А также
DurronTwo
И мой тестовый жгут: (Требуется Google Caliper 0.1-RC5)
ОБНОВЛЕНИЕ: я сделал новый алгоритм, который быстрее в некоторых сценариях, медленнее в других, я получил разные тесты, основанные на разных входах. Если мы вычислим по модулю
0xFFFFFF = 3 x 3 x 5 x 7 x 13 x 17 x 241
, мы можем исключить 97,82% чисел, которые не могут быть квадратами. Это может быть (вроде) сделано в одной строке, с 5 побитовыми операциями:Полученный индекс - это либо 1) остаток, 2) остаток
+ 0xFFFFFF
, либо 3) остаток+ 0x1FFFFFE
. Конечно, нам нужно иметь справочную таблицу для остатков по модулю0xFFFFFF
, которая составляет около 3 МБ файла (в данном случае она хранится в виде десятичных чисел в тексте ascii, не оптимально, но явно с точностью до aByteBuffer
и т. Д. Но, поскольку это предварительный расчет, это не так) Это не имеет большого значения. Вы можете найти файл здесь (или создать его самостоятельно):Я загружаю его в
boolean
массив следующим образом:Пример времени выполнения. Он победил
Durron
(первая версия) в каждом испытании, которое я проводил.источник
sqrtps
пропускная способность SIMD или дажеsqrtpd
(двойная точность) не так уж и плоха для Skylake, но не намного лучше, чем задержка на старых процессорах. В любом случае 7-cpu.com/cpu/Haswell.html имеет несколько хороших экспериментальных номеров и страниц для других процессоров. В справочнике по микроархам Agner Fog pdf приведены некоторые значения задержки кэша для Intel и AMD: agner.org/optimizedouble
точность, чтобы избежать округления некоторого целого числа вне диапазона + -2 ^ 24 (таким образом, 32-разрядное целое число может быть вне этого), иsqrtpd
оно медленнее, чемsqrtps
обработка только половины числа элементов на инструкцию (для вектора SIMD) ,Должно быть намного быстрее использовать метод Ньютона для вычисления корня целочисленного квадрата , затем возвести в квадрат это число и проверить, как вы делаете в своем текущем решении. Метод Ньютона является основой для решения Кармака, упомянутого в некоторых других ответах. Вы должны быть в состоянии получить более быстрый ответ, поскольку вас интересует только целочисленная часть корня, что позволяет вам быстрее остановить алгоритм аппроксимации.
Еще одна оптимизация, которую вы можете попробовать: если цифровой корень числа не заканчивается на 1, 4, 7 или 9, число не является идеальным квадратом. Это можно использовать как быстрый способ устранения 60% ваших входных данных перед применением более медленного алгоритма квадратного корня.
источник
Math.sqrt()
работает с двойными значениями в качестве входных параметров, поэтому вы не получите точных результатов для целых чисел больше 2 ^ 53 .источник
Просто для записи, другой подход заключается в использовании простого разложения. Если каждый фактор разложения четный, то число является идеальным квадратом. Итак, вы хотите увидеть, можно ли разложить число как произведение квадратов простых чисел. Конечно, вам не нужно получать такое разложение, просто чтобы увидеть, существует ли оно.
Сначала создайте таблицу квадратов простых чисел, которые меньше, чем 2 ^ 32. Это намного меньше, чем таблица всех целых чисел до этого предела.
Решение тогда будет таким:
Я думаю, это немного загадочно. На каждом шаге он проверяет, что квадрат простого числа делит входное число. Если это так, то он делит число на квадрат как можно дольше, чтобы удалить этот квадрат из простого разложения. Если в результате этого процесса мы пришли к 1, то входное число было разложением квадрата простых чисел. Если квадрат становится больше, чем само число, то этот квадрат или любые большие квадраты не могут его разделить, поэтому число не может быть разложением квадратов простых чисел.
Учитывая, что в настоящее время sqrt выполняется аппаратно, и здесь необходимо вычислять простые числа, я думаю, что это решение намного медленнее. Но это должно дать лучшие результаты, чем решение с sqrt, которое не будет работать более 2 ^ 54, как говорит mrzl в своем ответе.
источник
sqrtsd
пропускная способность Core2 составляет один на 6-58с. Этоidiv
один на 12-36 циклов. (задержки аналогичны пропускной способности: ни одна единица не конвейерная).Было отмечено, что последние
d
цифры идеального квадрата могут принимать только определенные значения. Последниеd
цифры (в базеb
) числаn
такие же, как и остальные, когдаn
делятся наb
d
, т.е. в нотацииn % pow(b, d)
.Это может быть обобщено на любой модуль
m
, т.е.n % m
может использоваться для исключения некоторого процента чисел из идеальных квадратов. Модуль, который вы используете в настоящее время, равен 64, что позволяет 12, т.е. 19% остатков, как возможные квадраты. С небольшим кодированием я нашел модуль 110880, который позволяет только 2016, т.е. 1,8% остатков в качестве возможных квадратов. Таким образом, в зависимости от стоимости операции модуля (т. Е. Деления) и поиска в таблице по сравнению с квадратным корнем на вашей машине, использование этого модуля может быть быстрее.Кстати, если у Java есть способ хранить упакованный массив битов для таблицы поиска, не используйте его. В наши дни 110880 32-разрядных слов - это не много ОЗУ, и выбор машинного слова будет быстрее, чем выборка одного бита.
источник
idiv
) равно или хуже по стоимости FP sqrt (sqrtsd
) на текущем оборудовании x86. Кроме того, полностью не согласен с избеганием битовых полей. Частота попаданий в кэш будет намного лучше при использовании битового поля, а тестирование в битовом поле - всего одна или две более простые инструкции, чем тестирование целого байта. (Для крошечных таблиц, которые помещаются в кэш даже в виде не битовых полей, лучше использовать байтовый массив, а не 32-битные. X86 имеет однобайтовый доступ с равной скоростью до 32-битного слова.)Целочисленная задача заслуживает целочисленного решения. таким образом
Выполните бинарный поиск по (неотрицательным) целым числам, чтобы найти наибольшее целое число t, такое что
t**2 <= n
. Тогда проверить,r**2 = n
точно ли . Это занимает время O (log n).Если вы не знаете, как выполнить двоичный поиск натуральных чисел, потому что множество не ограничено, это легко. Вы начинаете с вычисления возрастающей функции f (выше
f(t) = t**2 - n
) по степеням два. Когда вы видите, что это становится положительным, вы нашли верхнюю границу. Тогда вы можете сделать стандартный бинарный поиск.источник
O((log n)^2)
потому что умножение не является постоянным временем, но на самом деле имеет нижнюю границуO(log n)
, которая становится очевидной при работе с большими числами с высокой точностью. Но объем этой вики кажется 64-битным, так что, возможно, это nbd.Следующее упрощение решения maaartinus, похоже, позволяет сократить время выполнения на несколько процентных пунктов, но я недостаточно хорош в тестировании, чтобы произвести тест, которому я могу доверять:
Стоит проверить, как пропустить первый тест,
повлияет на производительность.
источник
Для производительности вам очень часто приходится идти на некоторые компромиссы. Другие выражали различные методы, однако вы заметили, что хак Кармака был быстрее до определенных значений N. Затем вы должны проверить «n», и если оно меньше, чем число N, используйте хак Кармака, иначе используйте какой-то другой описанный метод. в ответах здесь.
источник
Это самая быстрая реализация Java, которую я мог придумать, используя комбинацию методов, предложенных другими в этой теме.
Я также экспериментировал с этими модификациями, но они не помогли производительности:
источник
Вы должны избавиться от 2-степенной части N с самого начала.
2nd Edit Волшебное выражение для м ниже должно быть
а не как написано
Конец 2-го редактирования
1-е редактирование:
Незначительное улучшение:
Конец первого редактирования
Теперь продолжайте как обычно. Таким образом, к тому времени, когда вы доберетесь до части с плавающей запятой, вы уже избавились от всех чисел, чья 2-степенная часть нечетна (примерно половина), и тогда вы будете считать только 1/8 того, что осталось. Т.е. вы запускаете часть с плавающей запятой на 6% чисел.
источник
Project Euler упоминается в тегах, и многие проблемы в нем требуют проверки номера >>
2^64
. Большинство упомянутых выше оптимизаций не работают легко, когда вы работаете с 80-байтовым буфером.Я использовал java BigInteger и слегка модифицированную версию метода Ньютона, которая лучше работает с целыми числами. Проблема заключалась в том, что точные квадраты
n^2
сходились,(n-1)
а неn
потому,n^2-1 = (n-1)(n+1)
что конечная ошибка была всего на один шаг ниже конечного делителя, и алгоритм завершался. Это было легко исправить, добавив один к исходному аргументу перед вычислением ошибки. (Добавьте два для кубических корней и т. Д.)Одним из приятных атрибутов этого алгоритма является то, что вы можете сразу сказать, является ли число идеальным квадратом - конечная ошибка (не коррекция) в методе Ньютона будет равна нулю. Простая модификация также позволяет быстро вычислять
floor(sqrt(x))
вместо ближайшего целого числа. Это удобно с несколькими проблемами Эйлера.источник
Это доработка от десятичного к двоичному алгоритму старого калькулятора Марчанта (извините, у меня нет ссылки) в Ruby, адаптированном специально для этого вопроса:
Вот пример чего-то похожего (пожалуйста, не голосуйте за стиль кодирования / запахи или неуклюжий O / O - это алгоритм, который имеет значение, а C ++ не мой родной язык). В этом случае мы ищем остаток == 0:
источник
Как уже упоминалось, вызов sqrt не совсем точен, но он интересен и поучителен, так как он не отбрасывает другие ответы с точки зрения скорости. В конце концов, последовательность инструкций на ассемблере для sqrt крошечная. У Intel есть аппаратная инструкция, которая не используется Java, я считаю, потому что она не соответствует IEEE.
Так почему же это медленно? Потому что Java на самом деле вызывает подпрограмму C через JNI, и это на самом деле медленнее, чем вызов подпрограммы Java, которая сама по себе медленнее, чем встроенная. Это очень раздражает, и Java должна была придумать лучшее решение, то есть, при необходимости, создание вызовов библиотеки с плавающей запятой. Ну что ж.
Я подозреваю, что в C ++ все сложные альтернативы будут терять скорость, но я не проверял их все. То, что я сделал, и что люди Java найдут полезными, - это простой взлом, расширение тестирования специального случая, предложенного А. Рексом. Используйте одно длинное значение в качестве битового массива, который не проверяется по границам. Таким образом, у вас есть 64-битный логический поиск.
Процедура isPerfectSquare5 выполняется примерно на 1/3 времени на моей машине core2 duo. Я подозреваю, что дальнейшие изменения в том же направлении могут в среднем еще больше сократить время, но каждый раз, когда вы проверяете, вы тратите больше тестов на большее устранение, поэтому вы не можете идти слишком далеко по этому пути.
Конечно, вместо отдельного теста на отрицание вы можете проверить старшие 6 битов таким же образом.
Обратите внимание, что все, что я делаю, это устранение возможных квадратов, но когда у меня есть потенциальный случай, я должен вызвать исходный, встроенный isPerfectSquare.
Процедура init2 вызывается один раз для инициализации статических значений pp1 и pp2. Обратите внимание, что в моей реализации на C ++ я использую unsigned long long, поэтому, поскольку вы подписаны, вам придется использовать оператор >>>.
Нет необходимости в проверке массива, но оптимизатор Java должен довольно быстро разобраться с этим, поэтому я не виню их за это.
источник
pp2
? Я понимаю, чтоpp1
это используется для проверки шести младших разрядов, но я не верю, что проверка следующих шести разрядов имеет какой-то смысл.Мне нравится идея использовать почти правильный метод для некоторых входных данных. Вот версия с более высоким «смещением». Код, кажется, работает и проходит мой простой тестовый пример.
Просто замените ваш:
код с этим:
источник
Учитывая общую длину в битах (хотя я использовал здесь определенный тип), я попытался разработать упрощенный алгоритм, как показано ниже. Первоначально требуется простая и очевидная проверка для 0,1,2 или <0. Следующее просто в том смысле, что оно не пытается использовать какие-либо существующие математические функции. Большинство операторов могут быть заменены побитовыми операторами. Я не проверял ни с какими контрольными данными все же. Я не являюсь экспертом в математике или компьютерном алгоритме, в частности, я хотел бы, чтобы вы указали на проблему. Я знаю, что есть много шансов на улучшение.
источник
Я проверил все возможные результаты, когда наблюдаются последние n бит квадрата. Последовательно исследуя больше битов, можно исключить до 5/6 входных данных. Я на самом деле разработал это для реализации алгоритма факторизации Ферма, и он там очень быстрый.
Последний бит псевдокода можно использовать для расширения тестов, чтобы исключить больше значений. Вышеприведенные тесты для k = 0, 1, 2, 3
Сначала он проверяет наличие квадратного остатка с модулями степени два, затем тестирует на основе окончательного модуля, а затем использует Math.sqrt для окончательного тестирования. Я придумал идею из верхнего поста и попытался ее расширить. Я ценю любые комментарии или предложения.
Обновление. Используя тест по модулю (modSq) и базе модулей 44352, мой тест выполняется в 96% времени по сравнению с тестом в обновлении OP для чисел до 1 000 000 000.
источник
Вот решение «разделяй и властвуй».
Если корень квадратный из натурального числа (
number
) является натуральным числом (solution
), вы можете легко определить диапазон наsolution
основе количества цифрnumber
:number
имеет 1 цифру:solution
в диапазоне = 1 - 4number
имеет 2 цифры:solution
в диапазоне от 3 до 10number
имеет 3 цифры:solution
в диапазоне = 10 - 40number
имеет 4 цифры:solution
в диапазоне от 30 до 100number
имеет 5 цифр:solution
в диапазоне = 100 - 400Заметили повторение?
Вы можете использовать этот диапазон в подходе бинарного поиска, чтобы увидеть, есть ли
solution
для чего:Вот код
Вот мой класс SquareRootChecker
И вот пример того, как его использовать.
источник
toString
невероятно дорогой операции по сравнению с побитовыми операторами. Таким образом, чтобы удовлетворить цель вопроса - производительность - вы должны использовать побитовые операторы вместо базовых 10 строк. Опять же, мне очень нравится ваша концепция. Несмотря на это, ваша реализация (в том виде, в каком она существует сейчас) является самой медленной из всех возможных решений вопроса.Если скорость вызывает беспокойство, почему бы не выделить из наиболее часто используемых наборов входных данных и их значений таблицу поиска, а затем выполнить любой оптимизированный магический алгоритм, который вы придумали для исключительных случаев?
источник
Должна быть возможность упаковать 'не может быть идеальным квадратом, если последние X цифр N' гораздо эффективнее, чем это! Я буду использовать 32-битные числа Java и получу достаточно данных для проверки последних 16 битов числа - это 2048 шестнадцатеричных значений типа int.
...
Хорошо. Либо я столкнулся с некоторой теорией чисел, которая немного выше меня, либо в моем коде есть ошибка. В любом случае вот код:
и вот результаты:
(ed: исключен из-за низкой производительности в prettify.js; посмотреть историю изменений, чтобы увидеть.)
источник
Метод Ньютона с целочисленной арифметикой
Если вы хотите избежать нецелочисленных операций, вы можете использовать метод ниже. Он в основном использует метод Ньютона, модифицированный для целочисленной арифметики.
Эта реализация не может конкурировать с решениями, которые используют
Math.sqrt
. Однако его производительность может быть улучшена с помощью механизмов фильтрации, описанных в некоторых других публикациях.источник
Вычисление квадратных корней по методу Ньютона ужасно быстро ... при условии, что начальное значение разумно. Однако разумного начального значения не существует, и на практике мы заканчиваем разделение на две части и поведение log (2 ^ 64).
Чтобы быть действительно быстрым, нам нужен быстрый способ достичь разумного начального значения, а это значит, что нам нужно погрузиться в машинный язык. Если процессор предоставляет инструкцию типа POPCNT в Pentium, которая подсчитывает начальные нули, мы можем использовать ее, чтобы получить начальное значение с половиной значащих бит. С осторожностью мы можем найти фиксированное количество шагов Ньютона, которое всегда будет достаточно. (Таким образом, отпадает необходимость в цикле и очень быстром исполнении.)
Второе решение заключается в использовании функции с плавающей запятой, которая может иметь быстрое вычисление sqrt (как, например, сопроцессор i87). Даже экскурсия через exp () и log () может быть быстрее, чем Ньютон, вырождающийся в двоичный поиск. В этом есть один сложный аспект, зависящий от процессора анализ того, что и если впоследствии необходимо усовершенствовать.
Третье решение решает немного другую проблему, но стоит упомянуть, потому что ситуация описана в этом вопросе. Если вы хотите вычислить большое количество квадратных корней для чисел, которые немного отличаются, вы можете использовать итерацию Ньютона, если вы никогда не инициализируете начальное значение, а просто оставляете его там, где остановились предыдущие вычисления. Я успешно использовал это по крайней мере в одной проблеме Эйлера.
источник
Квадратный корень числа, учитывая, что число является идеальным квадратом.
Сложность журнала (п)
источник
Если вам нужна скорость, учитывая, что ваши целые числа имеют конечный размер, я подозреваю, что самый быстрый способ заключался бы в (а) разбиении параметров по размеру (например, на категории по наибольшему установленному биту), а затем проверке значения по массиву совершенных квадратов в этом диапазоне.
источник
Что касается метода Carmac, кажется, что было бы довольно легко просто повторить еще раз, что должно удвоить число цифр точности. В конце концов, это чрезвычайно укороченный итерационный метод - метод Ньютона, с очень хорошим первым предположением.
Что касается вашего текущего лучшего, я вижу две микрооптимизации:
То есть:
Еще лучше может быть простой
Очевидно, было бы интересно узнать, сколько номеров отбраковано на каждой контрольной точке - я скорее сомневаюсь, что проверки действительно независимы, что усложняет задачу.
источник