PL/SQL计算质数

2008-01-08 10:48:31|  分类: 默认分类 |  标签: |举报 |字号大中小 订阅

看到别人写的一片计算质数文章, 感觉不错;转过来学习一下。

目标很简单,列出100以内的质数。

其实算法很简单,两个循环就搞定了。但是发现使用不同的算法,执行效率差别之大相当惊人,特别是数据量级很大的时候。

下面就是最常见的一种写法:(也是最差的一种)

PHP code:


SQL> SET SERVEROUT ON

SQL> DECLARE

2   V_FLAG BOOLEAN;

3  BEGIN

4   FOR I IN 2 .. 100 LOOP

5    V_FLAG := TRUE;

6    FOR J IN 2 .. I - 1 LOOP

7     IF  MOD(I,J) = 0 THEN

8      V_FLAG := FALSE;

9     END IF;

10    END LOOP;

11

12    IF V_FLAG = TRUE THEN

13     DBMS_OUTPUT.PUT_LINE(I);

14    END IF;

15   END LOOP;

16  END;

17  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 00.09

.


由于屏幕输出操作比较慢,为了避免影响,将屏幕输出关闭。并将数据量增大到100000,以下所有的测试都在这个相同条件下进行。

PHP code:


SQL> DECLARE

2   V_FLAG BOOLEAN;

3  BEGIN

4   FOR I IN 2 .. 100000 LOOP

5    V_FLAG := TRUE;

6    FOR J IN 2 .. I - 1 LOOP

7     IF  MOD(I,J) = 0 THEN

8      V_FLAG := FALSE;

9     END IF;

10    END LOOP;

11

12    IF V_FLAG = TRUE THEN

13     --DBMS_OUTPUT.PUT_LINE(I);

14     NULL;

15    END IF;

16   END LOOP;

17  END;

18  /

PL/SQL 过程已成功完成。

已用时间:  02: 02: 58.73

.


这种方法在100000数据量的用时居然达到了2个小时。

如果稍微仔细考虑一下,就会发现,系统做了很多没有必要的工作,首先判断是否能整除的时候不需要循环到I – 1,只要执行到I的平方根就可以了,而且,如果I可以被整除就不需要继续循环,可以马上跳出内层循环了。经过简单优化后:

PHP code:


SQL> DECLARE

2   V_FLAG BOOLEAN;

3  BEGIN

4   FOR I IN 2 .. 100000 LOOP

5    V_FLAG := TRUE;

6    FOR J IN 2 .. TRUNC(POWER(I, 0.5)) LOOP

7     IF  MOD(I,J) = 0 THEN

8      V_FLAG := FALSE;

9      EXIT;

10     END IF;

11    END LOOP;

12

13    IF V_FLAG = TRUE THEN

14     --DBMS_OUTPUT.PUT_LINE(I);

15     NULL;

16    END IF;

17   END LOOP;

18  END;

19  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 16.21

.


效果十分的明显,从2个多小时,缩减到了16秒。

算法还可以进一步优化,考虑到质数中只有2是偶数,其他都是奇数,可以将2单独处理,然后将循环的步长设置为2,这样外层循环次数就减少了一半。

PHP code:


SQL> DECLARE

2   I NUMBER DEFAULT 3;

3   V_FLAG BOOLEAN;

4  BEGIN

5   --DBMS_OUTPUT.PUT_LINE(2);

6   WHILE I < 100000 LOOP

7    V_FLAG := TRUE;

8    FOR J IN 2 .. TRUNC(POWER(I, 0.5)) LOOP

9     IF  MOD(I,J) = 0 THEN

10      V_FLAG := FALSE;

11      EXIT;

12     END IF;

13    END LOOP;

14

15    IF V_FLAG = TRUE THEN

16     --DBMS_OUTPUT.PUT_LINE(I);

17     NULL;

18    END IF;

19   I := I + 2;

20   END LOOP;

21  END;

22  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 09.37

.


仔细考虑一下,其实用来被整除的数是质数就足够了,不需要对所有奇数进行判断。在下面的过程中,使用索引表来保存计算得到的所有的质数:

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   V_FLAG BOOLEAN;

5   V_CNT NUMBER;

6   I NUMBER DEFAULT 3;

7  BEGIN

8   V_RESULT(1) := 2;

9   --DBMS_OUTPUT.PUT_LINE(V_RESULT(1));

10   WHILE(I < 100000) LOOP

11    V_FLAG := TRUE;

12    V_CNT := V_RESULT.COUNT;

13    FOR J IN 1..V_CNT LOOP

14     IF V_RESULT(J) > POWER(I, 0.5) THEN

15      EXIT;

16     END IF;

17     IF MOD(I,V_RESULT(J)) = 0 THEN

18      V_FLAG := FALSE;

19      EXIT;

20     END IF;

21    END LOOP;

22    IF V_FLAG THEN

23    -- DBMS_OUTPUT.PUT_LINE(I);

24     V_RESULT(V_CNT+1) := I;

25    END IF;

26    I := I + 2;

27   END LOOP;

28  END;

29  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 06.68

.


已经将速度提高到了6秒左右,还能不能更快呢?注意到在最内层循环中调用了一个函数POWER(I, 0.5),下面将这个表达式转换一下,避免使用这个函数:

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   V_FLAG BOOLEAN;

5   I NUMBER DEFAULT 3;

6  BEGIN

7   --DBMS_OUTPUT.PUT_LINE(2);

8   V_RESULT(1) := 2;

9   WHILE(I < 100000) LOOP

10    V_FLAG := TRUE;

11    FOR J IN 1..V_RESULT.COUNT LOOP

12     IF V_RESULT(J) * V_RESULT(J) > I THEN

13      EXIT;

14     END IF;

15     IF MOD(I,V_RESULT(J)) = 0 THEN

16      V_FLAG := FALSE;

17      EXIT;

18     END IF;

19    END LOOP;

20    IF V_FLAG THEN

21    -- DBMS_OUTPUT.PUT_LINE(I);

22     V_RESULT(V_RESULT.COUNT + 1) := I;

23    END IF;

24    I := I + 2;

25   END LOOP;

26  END;

27  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 01.03

.


难以置信吧,一个执行两个小时的PL/SQL,通过算法的调整可以优化到了1秒。

其实能优化的地方还有很多,不过这些优化能带来的性能提升已经很小了。比如:

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   V_FLAG BOOLEAN;

5   I NUMBER DEFAULT 3;

6  BEGIN

7   --DBMS_OUTPUT.PUT_LINE(2);

8   V_RESULT(0) := 2;

9   WHILE(I < 100000) LOOP

10    V_FLAG := TRUE;

11    FOR J IN 1..V_RESULT.COUNT - 1 LOOP

12     IF V_RESULT(J) * V_RESULT(J) > I THEN

13      EXIT;

14     END IF;

15     IF MOD(I,V_RESULT(J)) = 0 THEN

16      V_FLAG := FALSE;

17      EXIT;

18     END IF;

19    END LOOP;

20    IF V_FLAG THEN

21    -- DBMS_OUTPUT.PUT_LINE(I);

22     V_RESULT(V_RESULT.COUNT) := I;

23    END IF;

24    I := I + 2;

25   END LOOP;

26  END;

27  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 00.96

.


由于从3开始步长为2,因此判断随后的质数的时候,没有必要用2去整除,而直接可以从3开始。

过程仍然可以进一步优化,可以省略掉不必要的赋值和判断语句:

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   I NUMBER DEFAULT 3;

5  BEGIN

6   --DBMS_OUTPUT.PUT_LINE(2);

7   V_RESULT(1) := 3;

8   WHILE(I < 100000) LOOP

9    FOR J IN 1..V_RESULT.COUNT LOOP

10     IF MOD(I,V_RESULT(J)) = 0 THEN

11      EXIT;

12     END IF;

13     IF V_RESULT(J) * V_RESULT(J) > I THEN

14     -- DBMS_OUTPUT.PUT_LINE(I);

15      V_RESULT(V_RESULT.COUNT + 1) := I;

16      EXIT;

17     END IF;

18    END LOOP;

19    I := I + 2;

20   END LOOP;

21   V_RESULT(0) := 2;

22  END;

23  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 00.96

.


但是在100000这个数量级已经看不出性能的差别了。正如Tom所说的,系统总是可以提高1%的性能,不过付出的代价会越来越大。

刚才测试发现,将MOD函数转换一下,性能还会有一个相对明显的提升

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   I NUMBER DEFAULT 3;

5  BEGIN

6   --DBMS_OUTPUT.PUT_LINE(2);

7   V_RESULT(1) := 3;

8   WHILE(I < 100000) LOOP

9    FOR J IN 1..V_RESULT.COUNT LOOP

10     IF V_RESULT(J) * V_RESULT(J) > I THEN

11      --DBMS_OUTPUT.PUT_LINE(I);

12      V_RESULT(V_RESULT.COUNT + 1) := I;

13      EXIT;

14     END IF;

15     IF TRUNC(I/V_RESULT(J)) = I/V_RESULT(J) THEN

16      EXIT;

17     END IF;

18    END LOOP;

19    I := I + 2;

20   END LOOP;

21   V_RESULT(0) := 2;

22  END;

23  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 00.87

.


再来一次尝试:

PHP code:


SQL> DECLARE

2   TYPE T_RECORD IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;

3   V_RESULT T_RECORD;

4   I NUMBER DEFAULT 3;

5   N NUMBER DEFAULT 0;

6  BEGIN

7   --DBMS_OUTPUT.PUT_LINE(2);

8   V_RESULT(1) := 3;

9   WHILE(I < 100000) LOOP

10    FOR J IN 1..V_RESULT.COUNT LOOP

11     IF V_RESULT(J) * V_RESULT(J) > I THEN

12      --DBMS_OUTPUT.PUT_LINE(I);

13      V_RESULT(V_RESULT.COUNT + 1) := I;

14      EXIT;

15     END IF;

16     IF TRUNC(I/V_RESULT(J)) = I/V_RESULT(J) THEN

17      EXIT;

18     END IF;

19    END LOOP;

20    IF N = 2 THEN

21     I := I + 4;

22     N := 1;

23    ELSE

24     I := I + 2;

25     N := N + 1;

26    END IF;

27   END LOOP;

28   V_RESULT(0) := 2;

29  END;

30  /

PL/SQL 过程已成功完成。

已用时间:  00: 00: 00.84

PL/SQL计算质数相关推荐

  1. oracle 偶数与奇数,在PL / SQL中计算数字中的奇数和偶数

    我们给定一个正整数数字,任务是使用PL / SQL计算数字中奇数和偶数的计数. PL / SQL是SQL与编程语言的过程功能的组合.它是由Oracle Corporation在90年代初开发的,目的是 ...

  2. Linux求100内的质数,用SQL计算100以内的质数

    以前写过一篇文章,描述如何使用PL/SQL来计算100以内的质数,今天重翻那篇文章的时候,突然想到,能不能用SQL来实现同样的功能. 其实这个功能用PLSQL实现最简单,思路也很清晰,判断一个数是否是 ...

  3. 如何用mysql求质数_PLSQL计算质数

    看到有人实现了一个计算质数的函数,就是效率有点差,贴一个以前写的计算质数的算法. 目标很简单,列出100以内的质数. 其实算法很简单,两个循环就搞定了.但是发现使用不同的算法,执行效率差别之大相当惊人 ...

  4. PL/SQL ——分页编程

    通过PL/SQL编程,编写分页存储过程.代码如下所示: 1 --PL/SQL开发编写分页代码 2 --创建包 3 create or replace package Page as 4 type te ...

  5. oracle bl编译,使用 PL/SQL 条件编译

    预处理器指令 指令由指令控制标记"$"和普通的 PL/SQL 文本组成.条件编译使用三个指令:选择.查询和错误.特殊的触发器字符"$"代表条件编译指令.选择指令 ...

  6. PL/SQL高级编程

    PL/SQL高级编程 实验目的: 1.掌握PL/SQL的数据结构和编程结构,掌握应用PL/SQL编写简单程序的方法 2.理解存储过程的概念,掌握编写存储过程的方法 3.理解函数的概念,掌握编写存储过程 ...

  7. PostgreSQL Oracle 兼容性之 - PL/SQL DETERMINISTIC 与PG函数稳定性(immutable, stable, volatile)...

    标签 PostgreSQL , Oracle , 函数稳定性 , stable , immutable , volatile , DETERMINISTIC 背景 Oracle创建pl/sql函数时, ...

  8. 判断题:oracle自带的sql语言环境是pl/sql,Oracle之PL/SQL学习笔记之数据类型(三)

    Oracle之PL/SQL学习笔记之数据类型(三) 所有的编程语言中变量是使用最频繁的.PL/SQL作为一个面向过程的数据库编程语言同样少不了变量,利用变量可以把PL/SQL块需要的参数传递进来,做到 ...

  9. ebs oracle pl sql开发_PL/SQL设置

    1.PL/SQL Developer记住登陆密码 再使用PL/SQL Developer时,为了工作方便希望PL/SQL Developer记住登录Oracle的用户名和密码: 设置方法:PL/SQL ...

最新文章

  1. javascript判断输入的值是不是正整数
  2. Java集合,ConcurrentHashMap底层实现和原理(常用于并发编程)
  3. c++枚举类型(二) c++11 枚举类
  4. [转]Windows Shell 编程 第十四章【来源:http://blog.csdn.net/wangqiulin123456/article/details/7988010】...
  5. 【Sqoop】sqoop导入导出
  6. windows11怎么绕过tpm安装,win11在线安装
  7. 在Windows上使用MSVC编译QuaZip
  8. $$a,PHP独有的可变变量
  9. Linux上Meson安装及使用
  10. java特别描述错误的是,关于javac命令作用的描述错误的是
  11. 一个Unity3D制作的坦克游戏——《燃烧的地平线》
  12. 微x怎么设置主题_团日活动主题策划书范文
  13. windows10 应用程序启动快捷键设置与取消
  14. HTML+CSS美食静态网页设计——简单牛排美食餐饮(9个页面)公司网站模板企业网站实现...
  15. 手机访问计算机FTP服务器
  16. onclick绑定点击事件不触发
  17. 英语不好可以学编程嘛?程序员必备英文单词汇总
  18. 学校计算机网络教室管理员职责,福建广播电视大学计算机网络教室管理人员工作职责...
  19. java:错误: 找不到符号
  20. html5 scrollheight,scrollHeight和scrollWidth,获取网页内容高度和宽度不正确

热门文章

  1. C++中的dynamic_cast和static_cast转化
  2. 直流无刷电机发热问题及解决方案
  3. 网站限制某些ip访问,仅允许某些ip…
  4. 最新会声会影2023旗舰版更新了哪些功能?
  5. mcnpf5输出结果_MCNP入门教程
  6. “有钱了”的理想汽车,能否拿下自动驾驶赛道入场券?
  7. SpringBoot——安全管理(一)
  8. C++11 find和find_if的用法
  9. 谷歌地球(Google Earth) 7.3.3.7721
  10. 数值模拟使用matlab实现案例