在Haskell中生成素数的有限列表

如何解决在Haskell中生成素数的有限列表

在Haskell中有很多有关生成质数的主题,但我认为它们都依赖于'isPrime'函数,如果我们尚不知道素数序列,则该函数应类似于:

isPrime k = if k > 1 then null [ x | x <- [2,3..(div k 2) + 1],k `mod` x == 0]
                     else False

({div可能会替换为sqrt,但仍然...)

我尝试根据“归纳定义”构造素数(假设我们有一组第一个 n 素数,然后是(n + 1)th 个素数是最小整数,因此第一个 n 质数都不是其除数)。我试图以斐波那契数列的方式来做到这一点,即:

fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

最后我得到了这个

-- checking if second number is a divisor of first one
ifDoesn'tDivide :: Int -> Int -> Bool
ifDoesn'tDivide n k 
    | mod n k == 0 = False
    | otherwise    = True

-- generating list which consists of first n prime numbers
firstPrimes :: Int -> [Int]
-- firstPrimes 1  = [2]
firstPrimes n     = take n primes 
    where primes = 2:(tail primes) ++ 
         [head [x | x <- [3,4..],k <- primes,ifDoesn'tDivide x k == True]]

但是它不起作用,当n >= 2堆栈溢出。关于如何解决它的任何建议?

“ Haskell可以根据自身定义数据结构,从而有效地创建了无限的数据结构” 。前面提到的质数和斐波那契序列是根据自身定义数据结构的特定情况,斐波那契序列工作得很好,但这些primes却不行。

我错过了什么吗,这两种算法在本质上是否有所不同?

P.S。所以,我认为,我只是在寻找大多数“ Haskellish”方式来做到这一点。

解决方法

您始终可以使用在Haskell中非常优雅的筛子。

<html>
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
  <style>
  body {
    position: relative; 
  }
  .affix {
    top:0;
    width: 100%;
    z-index: 9999 !important;
  }
  .navbar {
    margin-bottom: 0px;
  }

  .affix ~ .container-fluid {
   position: relative;
   top: 50px;
  }
  #section1 {padding-top:50px;height:500px;color: #fff; background-color: #1E88E5;}
  #section2 {padding-top:50px;height:500px;color: #fff; background-color: #673ab7;}
  #section3 {padding-top:50px;height:500px;color: #fff; background-color: #ff9800;}
  #section41 {padding-top:50px;height:500px;color: #fff; background-color: #00bcd4;}
  #section42 {padding-top:50px;height:500px;color: #fff; background-color: #009688;}
  </style>
</head>
<body data-spy="scroll" data-target=".navbar" data-offset="50">

<div class="container-fluid" style="background-color:#F44336;color:#fff;height:200px;">
  <h1>Scrollspy & Affix Example</h1>
  <h3>Fixed navbar on scroll</h3>
  <p>Scroll this page to see how the navbar behaves with data-spy="affix" and data-spy="scrollspy".</p>
  <p>The navbar is attached to the top of the page after you have scrolled a specified amount of pixels,and the links in the navbar are automatically updated based on scroll position.</p>
</div>

<nav class="navbar navbar-inverse" data-spy="affix" data-offset-top="197">
  <div class="container-fluid">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>                        
      </button>
      <a class="navbar-brand" href="#">WebSiteName</a>
    </div>
    <div>
      <div class="collapse navbar-collapse" id="myNavbar">
        <ul class="nav navbar-nav">
          <li><a href="#section1">Section 1</a></li>
          <li><a href="#section2">Section 2</a></li>
          <li><a href="#section3">Section 3</a></li>
          <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Section 4 <span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href="#section41">Section 4-1</a></li>
              <li><a href="#section42">Section 4-2</a></li>
            </ul>
          </li>
        </ul>
      </div>
    </div>
  </div>
</nav>    

<div id="section1" class="container-fluid">
  <h1>Section 1</h1>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
</div>
<div id="section2" class="container-fluid">
  <h1>Section 2</h1>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
</div>
<div id="section3" class="container-fluid">
  <h1>Section 3</h1>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
</div>
<div id="section41" class="container-fluid">
  <h1>Section 4 Submenu 1</h1>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
</div>
<div id="section42" class="container-fluid">
  <h1>Section 4 Submenu 2</h1>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
  <p>Try to scroll this section and look at the navigation bar while scrolling! Try to scroll this section and look at the navigation bar while scrolling!</p>
</div>

</body>
</html>

所以要得到前10个素数

primes = sieve [2..]

sieve (p : xs) = p : sieve [ x | x <- xs,x `mod` p > 0 ]

请注意,虽然未明确使用> take 10 primes [2,3,5,7,11,13,17,19,23,29] ,但列表理解可确保列表上的每个数字必须相对于其前面的所有素数均为素数。

这效率更高,它是Eratosthenes' sieve (编辑)的核心。

上面的代码是第一个示例:

本文详细介绍了Haskell中筛子的有效实施以及惰性在计算中的作用。强烈推荐!

,

解决方案尝试的核心是由以下各项提供的primes的无限列表:

primes = 2:(tail primes)
  ++ [head [x | x <- [3,4..],k <- primes,ifDoesn'tDivide x k == True]]

更新:您在一条评论中提到,您势在必行地考虑该算法,因此您在想象Haskell将使用tail primes的“当前”值,该值在为了评估类似[2] ++ [] ++ [3]之类的内容,然后循环。但是,当然,Haskell并不是必须的,因此不能这样工作。在Haskell中,primes具有一个固定的定义,该定义在程序执行期间保持不变。 Haskell程序可以逐渐“发现”(或更准确地说是“计算”)定义,这使我们首先可以根据自身来定义primes,但不能在更改过程中更改定义。执行。

因此,在查看此定义时,您需要假设primes以及因此tail primes出现的所有位置都具有相同的值,即使递归使用也是如此。这与采用参数的典型递归函数不同:

fact 0 = 1
fact n = n * fact (n-1)

在这里,即使函数 fact的定义在各处都相同,左侧的fact n的值和{{1的值}}由于参数不同,右侧可能会有所不同。

无论如何,如果我们以这个fact (n-1)的定义来看,我们需要primes作为所有素数的无限列表出现的所有地方(而不是随时间变化或“增长”的值),那么您可以了解为什么此定义无效。在这里,primes定义为primes,代表完成所有实际工作的复杂2 : tail primes ++ [expr],但是expr应该是无限的,因此在评估此表达式时,您将甚至不要获取tail primes,因为您将永远不会用尽列表expr

即使忽略tail primes位,因为++ [expr]具有单个固定定义,该表达式类似于:

primes

从自身上定义无限列表的正确方法不是正确的。问题在于primes = 2 : tail primes 的第二个元素被定义为primes的第一个元素,它是tail primes的第二个元素,因此primes的第二个元素是定义为本身。当Haskell尝试“发现” /“计算”其值时,这将创建一个无限循环。 primes定义的关键:

fibs

是先给出第一个和第二个元素,然后将第三个元素定义为前两个元素的总和-它不是根据自身定义的,而是根据先前列表的定义的元素。这是对无限(甚至是有限)列表成功进行递归定义的关键。

请注意,Haskell不会“知道” fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 是一个无限列表,并且对无限列表没有做任何特殊的事情。这与递归定义的有限列表的工作方式相同:

fib

关键还是在于,countdown = 10 : takeWhile (> 0) (map (subtract 1) countdown) 中的每个元素的定义方式仅取决于countdown previous 元素。

要修改您的countdown定义以这种方式工作,您可能想做的是概括列表理解,从获取“ 2”后的下一个质数到获取任何当前质数{{ 1}},基于primes可用:

p

这不能工作有两个原因。首先,由于primes是无限的,因此将永远检查不同的primeAfter p = head [x | x <- [p+1..],ifDoesn'tDivide x k] 值的可除性。我们需要对其进行修改,以仅检查素数primes直到当前素数k

k

这使用了一个帮助程序,该帮助程序将列表的开头一直到谓词为true的第一个元素:

p

第二,检查的结构不正确。如果存在任何素数primeAfter p = head [x | x <- [p+1..],k <- takeUntil (==p) primes,ifDoesn'tDivide x k] ,则列表推导将允许通过takeUntil p lst = case break p lst of (a,y:b) -> a ++ [y] 对其进行除法。仅当 all 素数x不除k时,我们才需要通过x

k

那么它就有机会工作了,我们可以将primeAfter p = head [x | x <- [p+1..],and [ifDoesn'tDivide x k | k <- takeWhile (<=p) primes]] 定义为:

primes

在这里,primes = go 2 where go p = p : go (primeAfter p) 将当前质数添加到列表中,然后使用go递归到下一个质数。之所以可行,是因为即使primeAfter访问由递归primeAfter p调用生成的无限列表primes,它也只会使用该列表直到当前素数{{ 1}},因此它会在尝试访问列表中自己的值之前停止,仅使用在调用go之前 生成的素数。

所以,这可行,我认为这很符合您最初尝试的精神:

p

正如@Mihalis所指出的,primeAfter p是Haskell中一个非常标准的示例,因此,对此也有更优雅的单行解决方案。

,

TL; DR:不,两种算法没有本质区别。


您的定义primes = 2:(tail primes) ++ ....表示head primes = 2head (tail primes) = head ((tail primes) ++ ....) = head (tail primes)。当然这是有问题的,会导致无限递归。


在保留代码意图的同时,对代码的最小修正可能是

firstPrimes1 :: Int -> [Int]
firstPrimes1 1  = [2]
firstPrimes1 n  = firstPrimes1 (n-1) ++ 
         take 1 [x | x <- [3,and [ mod x k > 0 | k <- firstPrimes1 (n-1)]]

(这将使用take 1 ...代替您的[head ...])。

令人难以置信的是它缓慢(looks指数级或更糟)。但这当然应该是

firstPrimes2 1  = [2]
firstPrimes2 n  = let { ps = firstPrimes2 (n-1) } in
       ps ++ 
         take 1 [x | x <- [3,and [ mod x k > 0 | k <- ps]]

现在非常慢,时间复杂度约为三次。但是,应该确实是

firstPrimes2b 2  = [2]
firstPrimes2b n  = let { ps = firstPrimes2b (n-1) } in
       ps ++ 
         take 1 [x | x <- [last ps+1..],and [ mod x k > 0 | k <- ps]]

现在behaves就像 quadratic 一样,确实也比其前身要快得多。

要像斐波那契流一样构造它,可以写为

primes3 = 2 : concatMap foo [1..]
  where
  foo k = let { ps = take k primes3 } in
          take 1 [ x | x <- [last ps+1..],and [ mod x k > 0 | k <- ps]]
-- or 
primes4 = 2 : concatMap bar (tail (inits primes4))
  where
  bar ps = take 1 [ x | x <- [last ps+1..],and [ mod x k > 0 | k <- ps]]
-- or even 
primes5 = 2 : [p | (ps,q) <- zip (tail (inits primes5)) primes5,p <- take 1 [ x | x <- [q+1..],and [ mod x k > 0 | k <- ps]]]

实际上看起来它遵循一种归纳模式,特别是 complete 又称​​“ strong” 归纳forall(n).(forall( k < n ).P(k)) => P(n)

因此,斐波那契计算与没有根本不同,尽管后者仅引用了前两个元素,而斐波那契计算则引用了所有 all 元素,同时添加了新的一个。但是就像斐波那契流一样,此序列也最终根据自身来定义:primes = ..... primes ......

inits使bar显式地引用先前已知的素数ps,同时在每个步骤中再添加一个 (由{ {1}} ),就像您想要的一样。 take 1收集每次调用concatMap产生的所有新的单元素段。

但是为什么那只能是一个素数?我们难道不能从已知的bar素数中安全地产生多于一个新素数的 吗?我们必须通过所有以上素数来真正地测试候选人,还是可以使用您在问题中也提到的众所周知的快捷方式?我们能否使其遵循完整的前缀归纳模式k,以便仅需 O(log log n)扩展步骤即可获得

我们可以从素数序列的每个前缀(当然,该序列始终保持相同)的每个步骤上生成更长的片段,因此对于每个候选而言,不是指所有前面的素数,但只占其中的一小部分?...


Erasatosnesnes在Haskell中最直接表达的真实筛子是

forall(n).(forall( k < floor(sqrt(n)) ).P(k)) => P(n)

minus具有明显的语义,即使不从data-ordlist包中加载,也很容易实现。)

尽管S. Horsley牧师在1772年(re?-)引入它时,(*)将Eratosthenes的筛子描述为等同于

import qualified Data.List.Ordered as O (minus)

primes = map head $ scanl (O.minus) [2..] [[p,p+p..] | p <- primes]

运行oprimes = map head $ scanl (O.minus . tail) [3,5..] [[p*p,p*p+2*p..] | p <- oprimes] primes2 = 2 : oprimes primesUpTo n = 2 : map head a ++ takeWhile (<= n) b where (a,b:_) = span ((<= n) . (^2) . head) $ scanl (O.minus . tail) [3,p*p+2*p..] | p <- oprimes] length $ primesUpTo n快得多。你知道为什么吗?

在访问第length . takeWhile (<= n) primes个元素时,是否可以修复primes2使其速度与primesUpTo一样快?它可以遵循您的原始思想,逐步扩展已知的素数部分,如上一节所述。

此外,请注意,这里根本没有使用n函数。这是Eratosthenes的 true 筛子的标志,该筛子不会测试素数,而是生成复合物,并免费获得复合物之间的素数。>


第一个isPrime代码的工作方式:它以序列scanl开头。然后,它会发出通知以从其中删除[2,4,...],并留下与[2,6,8,...]等价的内容,即 coprimes({2})

(即使列表是无限的,这也是可行的,因为Haskell的评估很懒惰-仅执行程序最终输出所需的计算量。)

然后它发出通知,从列表[3,9,...]中删除,并留下 coprimes({2,3})

在每个阶段,它将[3,12,..]从该时间点的序列中移出,并将该头部元素放在一边,从而形成素数序列。

(可以用head(或iterate等进行编码。)这是一个很好的练习,可以帮助弄清楚那里到底发生了什么。 ,您会看到您将重新创建素数序列,作为迭代 step 函数参数的一部分(第一个 k 素数的当前序列'互素数,以及下一个 k + 1 素数,以从该序列中删除 倍数。unfoldr版本指的是原始素数序列,从中依次取出素数,但这是同一回事。)

第二个scanl变体仅枚举素数的 odd 倍数,从素数的平方开始每个枚举(因此,例如对于 3 ,其为{{1} },对于 7 scanl)。不过,它仍会为每个素数而不是每个素数的平方开始枚举;这就是为什么它必须做出特殊安排才能在[9,15,21,27,...]函数中停止的情况下立即停止。这是its efficiency的关键。


(*)pg 314 of Philosophical Transactions,Vol.XIII.


另请参阅:[49,63,77,91,...]定义并使用了herehere

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-