如何在 Oracle SQL 中获取 1000-2000 之间的素数
How to get prime numbers between 1000-2000 in Oracle SQL
这是我的代码
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;
我收到一个错误:
Error starting at line : 2 in command - Error report - ORA-06550: line
18, column 4: PLS-00103: Encountered the symbol ";" when expecting one
of the following:
if
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
END;
您没有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;
/
一致的缩进使这种更明显,更容易追踪。
您还在错误的位置设置和检查您的标志 - 两者都应该在第一个循环内 - 并且您必须在开始之前初始化您的计数器,因为它将默认为 null:
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 关于您的算法的评论;该版本得到相同的答案 135,但效率更高(在我的系统上用十分之一的时间;大约 0.06 秒,而使用这种更简单的方法则为 0.60 秒)。
这个IF没有END IF
- IF IS_PRIME = 1 THEN
完整的 BEGIN...END
块没有 END;
。
错误告诉你必须采取什么行动 -
Action: END;
这通常意味着您必须将 END 关键字放在某处。
关于寻找素数算法的一些建议 -
检查质数时,不必循环到 num-1。循环直到 square-root(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;
/
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 以下的所有素数。由于在不到一秒的时间内生成完整结果,因此额外的工作无关紧要。
如果可能的话,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
);
这是我的代码
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;
我收到一个错误:
Error starting at line : 2 in command - Error report - ORA-06550: line 18, column 4: PLS-00103: Encountered the symbol ";" when expecting one of the following:
if 06550. 00000 - "line %s, column %s:\n%s" *Cause: Usually a PL/SQL compilation error. *Action: END;
您没有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;
/
一致的缩进使这种更明显,更容易追踪。
您还在错误的位置设置和检查您的标志 - 两者都应该在第一个循环内 - 并且您必须在开始之前初始化您的计数器,因为它将默认为 null:
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 关于您的算法的评论;该版本得到相同的答案 135,但效率更高(在我的系统上用十分之一的时间;大约 0.06 秒,而使用这种更简单的方法则为 0.60 秒)。
这个IF没有END IF
- IF IS_PRIME = 1 THEN
完整的 BEGIN...END
块没有 END;
。
错误告诉你必须采取什么行动 -
Action: END;
这通常意味着您必须将 END 关键字放在某处。
关于寻找素数算法的一些建议 -
检查质数时,不必循环到 num-1。循环直到 square-root(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;
/
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 以下的所有素数。由于在不到一秒的时间内生成完整结果,因此额外的工作无关紧要。
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
);