如何在 Oracle SQL 中获取 1000-2000 之间的素数

Posted

技术标签:

【中文标题】如何在 Oracle SQL 中获取 1000-2000 之间的素数【英文标题】:How to get prime numbers between 1000-2000 in Oracle SQL 【发布时间】:2015-02-18 16:04:00 【问题描述】:

这是我的代码

SET SERVEROUTPUT ON;
DECLARE
ACOUNTER INTEGER;
IS_PRIME INTEGER;
BEGIN
IS_PRIME := 1;
FOR NUM IN 1000..2000 LOOP
    FOR D IN 2..NUM-1 LOOP
        IF MOD(NUM,D) = 0 THEN
        IS_PRIME := 0;
        END IF;
      END LOOP; 
END LOOP;

IF IS_PRIME = 1 THEN
ACOUNTER := ACOUNTER +1;

DBMS_OUTPUT.PUT_LINE('THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: ' || ACOUNTER);
END;

我收到一个错误:

从第 2 行开始的错误命令 - 错误报告 - ORA-06550: 行 18、第4栏:PLS-00103:遇到符号“;”当期待一个 以下内容:

如果 06550. 00000 - “第 %s 行,第 %s 列:\n%s” *原因:通常是 PL/SQL 编译错误。 *行动: 结束;

【问题讨论】:

有什么问题? 您不会用; 终止每一行SQL。这是一个问题。 你缺少一个开始的结束条件;大概您在显示的代码之后有end; 编辑您的问题以修复问题所涉及的错误对以后出现的任何人都不是很有帮助。添加END; 是可以的,因为您已经拥有了,但是添加缺少的END IF 会使答案看起来无效。删除错误消息也无济于事,尽管我知道那不是你。 顺便说一句,在 PL/SQL 中实现此功能时,最好将 IS_PRIME 设为 boolean 【参考方案1】:

此 IF 没有 END IF - IF IS_PRIME = 1 THEN

完整的BEGIN...END 块没有END;

错误会告诉你必须采取什么行动 -

动作:结束;

这通常意味着您必须将 END 关键字放在某处。

关于寻找素数的算法的一些建议 -

    在检查素数时,您不必循环到 num-1。循环直到平方根(num)可以正常工作。 Proof

    当你找到一个非素数时,退出循环。无需检查该数字的任何其他除数。

编辑:你的逻辑错误会给你不正确的结果 -

    在第一个循环中,将变量 IS_PRIME 重新初始化为 1。

    计数器增量应该发生在第一个循环内。

这是正确的程序 -

SET SERVEROUTPUT ON;

DECLARE
   ACOUNTER   INTEGER;
   IS_PRIME   INTEGER;
BEGIN
   ACOUNTER   := 0;
   IS_PRIME   := 1;

   FOR NUM IN 1000 .. 2000
   LOOP
      IS_PRIME   := 1;

      FOR D IN 2 .. SQRT (num)
      LOOP
         IF MOD (NUM, D) = 0
         THEN
            IS_PRIME   := 0;
            EXIT;
         END IF;
      END LOOP;

      IF IS_PRIME = 1
      THEN
         ACOUNTER   := ACOUNTER + 1;
      END IF;
   END LOOP;

   DBMS_OUTPUT.PUT_LINE (
      'THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: ' || ACOUNTER);
END;
/

【讨论】:

感谢您的指点。这就是我今天早些时候学到的如何检查素数的方法。我将研究 sqaure-root 方法。 平方根方法起作用的原因是,如果一个数不是素数,那么它的至少一个因数将小于或等于它自己的平方根。【参考方案2】:

您没有END IF;鉴于报告错误的行号,您在显示的内容之后有一个END;,所以最后几行是:

...
IF IS_PRIME = 1 THEN
ACOUNTER := ACOUNTER +1;

DBMS_OUTPUT.PUT_LINE('THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: ' || ACOUNTER);
END;
/

当编译器看到END; 时,它期望前面的IF 被关闭,所以当它看到; 时会抛出异常。只需在素数检查后添加END IF

...
    IF IS_PRIME = 1 THEN
        ACOUNTER := ACOUNTER +1;
    END IF;

    DBMS_OUTPUT.PUT_LINE('THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: ' || ACOUNTER);
END;
/

一致的缩进使这种情况更明显,更容易追踪。

你还在错误的地方设置和检查你的标志——两者都应该在第一个循环内——你必须在开始之前初始化你的计数器,因为它将默认为空:

DECLARE
    ACOUNTER INTEGER := 0;
    IS_PRIME INTEGER;
BEGIN
    FOR NUM IN 1000..2000 LOOP
        IS_PRIME := 1;
        FOR D IN 2..NUM-1 LOOP
            IF MOD(NUM,D) = 0 THEN
                IS_PRIME := 0;
            END IF;
        END LOOP; 

        IF IS_PRIME = 1 THEN
            ACOUNTER := ACOUNTER +1;
        END IF;
    END LOOP;

    DBMS_OUTPUT.PUT_LINE('THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: ' || ACOUNTER);
END;
/

anonymous block completed
THE # OF PRIME NUMBERS BETWEEN 1000-2000 ARE: 135

不过,也请参阅@ruudvan 的 cmets 关于您的算法;该版本得到相同的答案 135,但效率更高(在我的系统上用十分之一的时间;大约 0.06 秒,而这种更简单的方法为 0.60 秒)。

【讨论】:

如果我得到更多错误代码,我已经继续尝试结束 @NgocPhan - 我猜不出你有什么错误,或者为什么;只需添加 END IF 即可编译并运行,尽管它得到了错误的结果。我也添加了一个修改过的块,移动了逻辑,现在得到了答案。不知道你做了什么不同的错误。 感谢您的更正。我明白我在逻辑上做错了什么。 查看我对错误结果的评论。没关系,我想你明白了。【参考方案3】:

Alex Poole 已经解决了您的代码的实际问题。但是,与许多问题一样,如果要在数据库中解决这个问题,最好使用 SQL。本着这种精神,这个问题的 SQL 解决方案随之而来。

WITH pc AS
        (SELECT *
         FROM   (SELECT     LEVEL AS numbers
                 FROM       DUAL
                 CONNECT BY LEVEL <= 2000)
         WHERE  (numbers = 2 OR MOD (numbers, 2) <> 0) AND numbers <> 1)
SELECT *
FROM   (SELECT numbers FROM pc
        MINUS
        SELECT pc1.numbers
        FROM   pc pc1
               JOIN pc pc2
                  ON     pc2.numbers <= CEIL (SQRT (pc1.numbers))
                     AND MOD (pc1.numbers, pc2.numbers) = 0)
WHERE  numbers BETWEEN 1000 AND 2000

connect by 生成 1 到 2000 之间的所有数字。使用公认的规则,我们可以消除所有大于 2 的偶数,并且只测试每个数字的模数,看是否低于该数字的平方根。

从技术上讲,此解决方案生成 2 到 2000 之间的每个素数,然后过滤掉 2000 以下的所有内容。由于 in 在一秒钟内产生完整的结果,因此额外的工作无关紧要。

【讨论】:

根据用例,这实际上可能会更好,而且它肯定是一个经过深思熟虑的解决方案。但是,PL/SQL 性能更好;将范围扩展到 1..10000,PL/SQL 仍然运行亚秒。但是,当我扩展此查询时,需要 12 秒以上才能返回。 @HepC:它似乎依赖于 PL/SQL 解决方案。对于 1...10,000,Alex Poole 的回答需要 19 秒,而 ruudvan 的回答需要 【参考方案4】:

如果可能的话,Always 是一个更好的选择,只使用 SQL 而不是 PLSQL

WITH A AS (
 SELECT LEVEL AS L FROM DUAL CONNECT BY LEVEL <= 2000
)
SELECT 
LISTAGG(L,', ') WITHIN GROUP (ORDER BY L)
FROM 
    (
        SELECT L FROM A MAIN
        WHERE L >= 1000 AND
        NOT EXISTS
        (
            SELECT 1 FROM A SUB
            WHERE 
            MAIN.L>SUB.L
            AND MOD(MAIN.L,L)=0
            AND SUB.L>1
        )
        AND L<>1
    );

【讨论】:

以上是关于如何在 Oracle SQL 中获取 1000-2000 之间的素数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 oracle pl/sql 查询中动态获取字段名称?

如何获取Oracle数据库中sql语句的执行时间

如何使用 PL-SQL 在 Oracle 中获取列数据类型

如何在动态 sql (ORACLE PLSQL) 中获取局部临时变量中的计数 (*) 值

如何在 Oracle SQL 中获取 1000-2000 之间的素数

如何在 Oracle PL/SQL 中动态地从行参数中获取命名列?