在Haskell中计算倍数(从C转换)?

在Haskell中计算倍数(从C转换)?,c,haskell,C,Haskell,我想写一个Haskell程序来计算倍数。基本上,当给定两个整数a和b时,我想知道有多少个整数是1≤ 毕≤ b是任意整数2的倍数≤ 人工智能≤ A.例如,如果a=3,b=30,我想知道1-30范围内有多少整数是2或3的倍数;有20个这样的整数:2,3,4,6,8,9,10,12,14,15,16,18,20,21,22,24,26,27,28,30 我有一个C程序可以实现这一点。我试图将其转换为Haskell,但部分困难在于绕过我使用的循环,因为Haskell不使用循环。我非常感谢所有帮助翻译这篇

我想写一个Haskell程序来计算倍数。基本上,当给定两个整数a和b时,我想知道有多少个整数是1≤ 毕≤ b是任意整数2的倍数≤ 人工智能≤ A.例如,如果a=3,b=30,我想知道1-30范围内有多少整数是2或3的倍数;有20个这样的整数:2,3,4,6,8,9,10,12,14,15,16,18,20,21,22,24,26,27,28,30

我有一个C程序可以实现这一点。我试图将其转换为Haskell,但部分困难在于绕过我使用的循环,因为Haskell不使用循环。我非常感谢所有帮助翻译这篇文章的人

我的C程序供参考(如果格式设置已关闭,则很抱歉):

#定义素数范围130
#定义素数32
#定义上限(10000000000000ull)//10^15
#定义最大基数乘以计数25000000
类型定义结构
{
焦素因子滞后;
长倍数;
}多重信息;
无符号字符primeFlag[PRIME_RANGE+1];
整数素数[PRIME_CNT];
int primeCnt=0;
int maxPrimeStart[PRIME_CNT];
multipleInfo baseMultiples[最大基数乘以计数];
multipleInfo mergedMultiples[最大基数乘以计数];
int basemultiplescont,mergedmultiplescont;
无效FindDoddMultiples(整数a、长b、长*计数);
无效生成集合(void);
无效合并列表(多信息列表源[],整数计数,多信息
listDest[],int*countD);
空隙筛(空隙);
内部主(空)
{
int i,j,a,n,startInd,endInd;
长b,倍数;
//生成素数
筛子();
素数[primeCnt]=素数范围+1;
generateBaseMultiples();
baseMultiples[basemultiplescont]。multiple=上限+1;
//输入和输出
scanf(“%d”和“&n”);
对于(i=1;i,必须使用替换循环

在(大多数)过程或面向对象的语言中,您几乎不应该(永远不?)使用递归。这是非常低效的,因为每次调用递归函数时都必须创建一个新的堆栈框架

然而,在函数式语言(如Haskell)中,编译器通常能够将递归优化到循环中,这使得它比过程式语言快得多

我已经将您的
sieve
函数转换为C语言中的一组递归函数。我将把它转换为Haskell:

int main(void) {
    //...
    int root = sqrt(PRIME_RANGE);
    primes[primeCnt++] = 2;

    sieve(3, PRIME_RANGE, root);
    //...
}

void sieve(int i, int end, int root) {
    if(i > end) {
        return;
    }

    if(!primeFlag[i]) {
        primes[primeCnt++] = i;

        if(root >= i) {
            markMultiples(i * i, PRIME_RANGE, i);
        }
    }

    i += 2;
    sieve(i, end, root);
}

void markMultiples(int j, int end, int prime) {
    if(j > end) {
        return;
    }

    primeFlag[j] = 1;
    j += i << 1;

    markMultiples(j, end, prime);
}
int main(无效){
//...
int root=sqrt(素数范围);
素数[primeCnt++]=2;
筛(3,基本范围,根);
//...
}
空隙筛(int i、int end、int root){
如果(i>结束){
返回;
}
if(!primeFlag[i]){
素数[primeCnt++]=i;
如果(根>=i){
标记倍数(i*i,素数范围,i);
}
}
i+=2;
筛(i、端部、根部);
}
无效标记倍数(整数j、整数结束、整数素数){
如果(j>结束){
返回;
}
primeFlag[j]=1;
j+=i您必须使用替代循环

在(大多数)过程或面向对象的语言中,您几乎不应该(永远不?)使用递归。这是非常低效的,因为每次调用递归函数时都必须创建一个新的堆栈框架

然而,在函数式语言(如Haskell)中,编译器通常能够将递归优化到循环中,这使得它比过程式语言快得多

我已经将您的
sieve
函数转换为C语言中的一组递归函数。我将把它转换为Haskell:

int main(void) {
    //...
    int root = sqrt(PRIME_RANGE);
    primes[primeCnt++] = 2;

    sieve(3, PRIME_RANGE, root);
    //...
}

void sieve(int i, int end, int root) {
    if(i > end) {
        return;
    }

    if(!primeFlag[i]) {
        primes[primeCnt++] = i;

        if(root >= i) {
            markMultiples(i * i, PRIME_RANGE, i);
        }
    }

    i += 2;
    sieve(i, end, root);
}

void markMultiples(int j, int end, int prime) {
    if(j > end) {
        return;
    }

    primeFlag[j] = 1;
    j += i << 1;

    markMultiples(j, end, prime);
}
int main(无效){
//...
int root=sqrt(素数范围);
素数[primeCnt++]=2;
筛(3,基本范围,根);
//...
}
空隙筛(int i、int end、int root){
如果(i>结束){
返回;
}
if(!primeFlag[i]){
素数[primeCnt++]=i;
如果(根>=i){
标记倍数(i*i,素数范围,i);
}
}
i+=2;
筛(i、端部、根部);
}
无效标记倍数(整数j、整数结束、整数素数){
如果(j>结束){
返回;
}
primeFlag[j]=1;
首先,除非我有严重误解,否则这里的倍数是错误的。1到30之间的2的倍数是15,1到30之间的3的倍数是10,所以这里应该有25个数字

编辑:我确实误解了;你想要唯一的倍数

要获得唯一的倍数,可以使用Data.Set,它具有不变的特性,即集合的元素是唯一的并按升序排列

如果你知道你不会超过
x=maxBound::Int
,你可以使用Data.IntSet获得更好的加速。我还包括了一些测试用例,并用注释说明了它们在我的机器上运行的目的

{-# LANGUAGE BangPatterns #-}

{-# OPTIONS_GHC -O2 #-}

module Main (main) where

import System.CPUTime (getCPUTime)
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet

main :: IO ()
main = do
  test 3 30       -- 0.12 ms
  test 131 132    -- 0.14 ms
  test 500 300000 -- 117.63 ms

test :: Int -> Int -> IO ()
test !a !b = do
  start <- getCPUTime
  print (numMultiples a b)
  end   <- getCPUTime
  print $ "Needed " ++ show ((fromIntegral (end - start)) / 10^9) ++ " ms.\n"

numMultiples :: Int -> Int -> Int
numMultiples !a !b = IntSet.size (foldMap go [2..a])
  where
    go :: Int -> IntSet 
    go !x = IntSet.fromAscList [x, x+x .. b]
{-#语言模式}
{-#选项(GHC-O2#-}
主模块(Main),其中
导入System.CPUTime(getCPUTime)
导入Data.IntSet(IntSet)
将限定的Data.IntSet作为IntSet导入
main::IO()
main=do
测试3 30-0.12毫秒
测试131 132--0.14毫秒
测试500 300000--117.63毫秒
测试::Int->Int->IO()
测试!a!b=do
起始整数
numMultiples!a!b=IntSet.size(foldMap go[2..a])
哪里
go::Int->IntSet
go!x=IntSet.fromAscList[x,x+x..b]
首先,除非我有严重误解,否则这里的倍数是错误的。1到30之间的2的倍数是15,1到30之间的3的倍数是10,所以这里应该有25个数字

编辑:我确实误解了;你想要唯一的倍数

要获得唯一的倍数,可以使用Data.Set,它具有不变的特性,即集合的元素是唯一的并按升序排列

如果你知道你不会超过
x=maxBound::Int
,你可以使用Data.IntSet获得更好的加速。我还包括了一些测试用例,并用注释说明了它们在我的机器上运行的目的

{-# LANGUAGE BangPatterns #-}

{-# OPTIONS_GHC -O2 #-}

module Main (main) where

import System.CPUTime (getCPUTime)
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet

main :: IO ()
main = do
  test 3 30       -- 0.12 ms
  test 131 132    -- 0.14 ms
  test 500 300000 -- 117.63 ms

test :: Int -> Int -> IO ()
test !a !b = do
  start <- getCPUTime
  print (numMultiples a b)
  end   <- getCPUTime
  print $ "Needed " ++ show ((fromIntegral (end - start)) / 10^9) ++ " ms.\n"

numMultiples :: Int -> Int -> Int
numMultiples !a !b = IntSet.size (foldMap go [2..a])
  where
    go :: Int -> IntSet 
    go !x = IntSet.fromAscList [x, x+x .. b]
{-#语言模式}
{-#选项(GHC-O2#-}
主模块(Main),其中
进口
~> f 100 (10^5)
88169
(0.05 secs, 48855072 bytes)

~> f 131 (3*10^6)
2659571
(0.55 secs, 1493586480 bytes)

~> f 131 132
131
(0.00 secs, 0 bytes)

~> f 500 300000
274055
(0.11 secs, 192704760 bytes)