Какой способ определить, является ли Int идеальным квадратом в Haskell?
Мне нужна простая функция
is_square :: Int -> Bool
который определяет, является ли Int N совершенным квадратом (существует целое число x такое, что x * x = N).
Конечно, я могу просто написать что-то вроде
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
но это выглядит ужасно! Может быть, существует простой простой способ реализовать такой предикат?
Ответы
Ответ 1
О, сегодня мне нужно было определить, является ли число идеальным кубом, и подобное решение было ОЧЕНЬ медленным.
Итак, я придумал довольно умную альтернативу
cubes = map (\x -> x*x*x) [1..]
is_cube n = n == (head $ dropWhile (<n) cubes)
Очень просто. Я думаю, мне нужно использовать дерево для более быстрого поиска, но теперь я попробую это решение, возможно, оно будет достаточно быстрым для моей задачи. Если нет, я отредактирую ответ с правильной структурой данных
Ответ 2
Подумайте об этом так, если у вас есть положительный int n
, тогда вы в основном выполняете двоичный поиск по диапазону чисел от 1.. n, чтобы найти первое число n'
, где n' * n' = n
.
Я не знаю Haskell, но этот F # должен быть легко преобразован:
let is_perfect_square n =
let rec binary_search low high =
let mid = (high + low) / 2
let midSquare = mid * mid
if low > high then false
elif n = midSquare then true
else if n < midSquare then binary_search low (mid - 1)
else binary_search (mid + 1) high
binary_search 1 n
Гарантировано быть O (log n). Легко модифицировать идеальные кубики и высшие силы.
Ответ 3
Существует замечательная библиотека для большинства связанных с теорией чисел проблем в Haskell, включенных в пакет arithmoi
.
Используйте библиотеку Math.NumberTheory.Powers.Squares
.
В частности, функция isSquare'
.
is_square :: Int -> Bool
is_square = isSquare' . fromIntegral
Библиотека оптимизирована и хорошо проверена людьми, гораздо более ориентированными на эффективность, чем вы или я. В то время как в настоящее время у нее нет такого рода махинаций капюшон, он может в будущем, когда библиотека развивается и становится более оптимизированной. Просмотрите исходный код, чтобы понять, как он работает!
Не изобретайте колесо, всегда используйте библиотеку, если она доступна.
Ответ 4
Я думаю, что код, который вы предоставили, самый быстрый, который вы получите:
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
Сложность этого кода: один sqrt, одно двойное умножение, один листинг (dbl- > int) и одно сравнение. Вы можете попытаться использовать другие методы вычислений для замены sqrt и умножения только с помощью целочисленной арифметики и сдвигов, но, скорее всего, это не будет быстрее, чем одно sqrt и одно умножение.
Единственное место, где стоит использовать другой метод, - это то, что процессор, на котором вы работаете, не поддерживает арифметику с плавающей запятой. В этом случае компилятору, вероятно, придется генерировать sqrt и двойное умножение в программном обеспечении, и вы можете получить преимущество в оптимизации для вашего конкретного приложения.
Как указано в другом ответе, все еще существует ограничение больших целых чисел, но если вы не собираетесь запускать эти числа, вероятно, лучше воспользоваться поддержкой аппаратных средств с плавающей запятой, чем писать собственный алгоритм.
Ответ 5
Wikipedia статья о Integer Square Roots позволяет алгоритмы адаптироваться в соответствии с вашими потребностями. Метод Ньютона хорош, потому что он сходится квадратично, т.е. Вы получаете в два раза больше правильных цифр на каждом шаге.
Я бы посоветовал вам держаться подальше от Double
, если ввод может быть больше, чем 2^53
, после чего не все целые числа могут быть точно представлены как Double
.
Ответ 6
Иногда вы не должны делить проблемы на слишком мелкие части (например, чеки is_square
):
intersectSorted [] _ = []
intersectSorted _ [] = []
intersectSorted xs (y:ys) | head xs > y = intersectSorted xs ys
intersectSorted (x:xs) ys | head ys > x = intersectSorted xs ys
intersectSorted (x:xs) (y:ys) | x == y = x : intersectSorted xs ys
squares = [x*x | x <- [ 1..]]
weird = [2*x+1 | x <- [ 1..]]
perfectSquareWeird = intersectSorted squares weird
Ответ 7
Там очень простой способ проверить идеальный квадрат - в буквальном смысле, вы проверяете, имеет ли квадратный корень из числа ничего, кроме нуля, в его дробной части.
Я предполагаю функцию квадратного корня, которая возвращает плавающую точку, и в этом случае вы можете сделать (Psuedocode):
func IsSquare(N)
sq = sqrt(N)
return (sq modulus 1.0) equals 0.0
Ответ 8
В комментарии к другому ответу на этот вопрос вы обсудили memoization. Имейте в виду, что этот метод помогает, когда ваши образцы зондов обладают хорошей плотностью. В этом случае это будет означать тестирование одних и тех же целых чисел снова и снова. Насколько вероятен ваш код для повторения одной и той же работы и, таким образом, выгоды от кеширования ответов?
Вы не дали нам представления о распределении ваших ресурсов, поэтому рассмотрите быстрый тест, в котором используется отличный criterion пакет:
module Main
where
import Criterion.Main
import Random
is_square n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n::Double)
is_square_mem =
let check n = sq * sq == n
where sq = floor $ sqrt $ (fromIntegral n :: Double)
in (map check [0..] !!)
main = do
g <- newStdGen
let rs = take 10000 $ randomRs (0,1000::Int) g
direct = map is_square
memo = map is_square_mem
defaultMain [ bench "direct" $ whnf direct rs
, bench "memo" $ whnf memo rs
]
Эта рабочая нагрузка может быть или не быть справедливым представителем того, что вы делаете, но, как написано, скорость пропуска кеша слишком высока:
![timing probability-density]()
Ответ 9
Это не особенно красиво или быстро, но здесь бесплатная версия без FPA, основанная на методе Ньютона, которая работает (медленно) для сколь угодно больших целых чисел:
import Control.Applicative ((<*>))
import Control.Monad (join)
import Data.Ratio ((%))
isSquare = (==) =<< (^2) . floor . (join g <*> join f) . (%1)
where
f n x = (x + n / x) / 2
g n x y | abs (x - y) > 1 = g n y $ f n y
| otherwise = y
Вероятно, это может быть вызвано некоторой дополнительной теорией теории чисел.