//题目:输入一个大于3的整数n,判定它是否为素数(prime,又称质数)
#include <stdio.h>
#include <math.h>
int main()
{int n,i,k;
  printf("please enter a integer number,n=?");
  scanf("%d",&n);
  k=sqrt(n);
  for(i=2;i<=k;i++)
    if(n%i==0)break;
  if(i<=k)printf("%d is not a prime number.\n",n);
  else printf("%d is a prime number.\n",n);
    return 0;
}

  ————谭浩强.《C程序设计》(第四版).p137,清华大学出版社

  这个程序的代码首先输入需要判断的整数并存放在变量n中(其实这个变量定义为unsigned类型更理想,因为题目中已经明确是大于3的整数)。紧接着通过:

k=sqrt(n);

  试图求出n的平方根或其平方根的整数部分。然而这个写法是有问题的,能否正确地按照希望求得n的平方根或其整数部分是得不到保证的。

  这是因为sqrt()函数的函数原型是:

double sqrt (double);

  也就是说,sqrt()函数的参数和返回值都是double数据类型。double类型属于浮点数据类型的一种,浮点类型并非像整数类型那样可以精确地表示整数,正相反,浮点数据类型用于近似地表示实数,尽管在个别情况下可以精确地表示,但就一般意义上而言,浮点数据类型只是对一定范围内的实数的一种近似表示。

  因此,在k=sqrt(n)这个表达式中,不可能指望这个n是准确的,因为按照sqrt()函数原型的要求,这个n实际上表示的是(double)n,即调用时会存在类型转换,转换后的值为double类型,它是否精确等于原来的int类型的n值,一般而言是不清楚的。

  或许有些对IEEE754标准很熟悉的人对此不能赞同——他们非常清楚在这个标准下浮点数的表示方法和内部结构。好吧,“不争论”,继续看下一个不确定性。

  由于sqrt()函数的返回值是double类型,这表明sqrt()函数只承诺为我们求得实参的平方根的近似值——而不是像数学那样一定可以得到一个精确值。换句话说,当调用sqrt(9.)的时候,函数未必会精确地得到3.0这个数学上的精确的平方根,sqrt(9.)的值无论为3.000000000000001还是2.999999999999999 都有可能。这虽然是出于理性的判断,但也并非绝无经验事实作为佐证。试看下面的代码:

#include <stdio.h>
#include <math.h> int main( void )
{
int p , n = 4 ; p = pow( 10 , n ); printf("%d\n", p ); return 0;
}

  在某些编译器上的输出结果是:

9999

  这清楚地表明pow()这样的返回值为double类型的数学函数只是一种近似计算的函数。sqrt()这个数学函数也是如此,sqrt(9.)的值无论为3.0、3.000000000000001还是2.999999999999999都有可能。一旦sqrt(9.)的值为2.999999999999999,那么

k=sqrt(n);

  将使k被赋值为2而不是预期中的3。这样代码中的下一句:

for(i=2;i<=k;i++)

的循环次数就会产生错误。这种错误在何时出现很难预料,但k=sqrt(n)是一个似是而非的表达则是确切无疑的。

  这种未必立刻发作的错误比那些立刻就产生症状的BUG更可怕,它就像一颗不定时炸弹一样说不定什么时候造成损失。古代有则守株待兔的寓言,说的是某一天出现了兔子,你不能指望天天出现兔子。但这里的情形恰恰相反,尽管很多天都没出现兔子,但你保不准哪天就会突然窜出一只兔子。而一旦出现兔子,其严重后果则是你假定不会出现兔子时所无法预料的。

  那么此时应该如何求n的平方根或其整数部分呢?实际上可以利用下面的数学常识轻易求得:

1=1^2
1+3=2^2
1+3+5=3^2

1+3+5+…+(2k1)=k^2

后面正确的代码应用了这个原理。

  样本代码中的另一个问题是根本没有考虑输入一旦不小于3时应该如何处理,这样的一个严重后果是一旦用户误输入数据,比如输入了负数,程序将会发生悲惨的崩溃。在一个真正的软件中,绝对不能假设用户一定会正确地输入,否则可能带来非常严重且无法弥补的损失。

  此外,代码中的输出部分过于啰唆繁复,后面的代码对此也进行了修正。

#include <stdlib.h>

int main( void )
{
int n ; printf("请输入n的值\n");
scanf("%d",&n); if( n <= 3)
printf("输入不正确,程序退出\n");
else
{
int n_ = n , odd = 1 , k = 0 , i ;
while( n_ >= odd )
{
n_ -= odd ;
odd += 2 ;
k ++ ;
} for(i=2;i<=k;i++)
if(n%i==0)
break; printf("%d%s是素数\n" , n , (i > k)?"":"不");
}
return 0;
}

  其中:

     while( n_ >=  odd )
{
n_ -= odd ;
odd += 2 ;
k ++ ;
}

  用于求出n的平方根的整数部分。由于只需进行√n次整数的加减法运算,其效率方面也必定高于浮点运算的k=sqrt(n)。

似是而非的k=sqrt(n)的更多相关文章

  1. 要心中有“数”——C语言初学者代码中的常见错误与瑕疵(8)

    在 C语言初学者代码中的常见错误与瑕疵(7) 中,我给出的重构代码中存在BUG.这个BUG是在飞鸟_Asuka网友指出“是不是时间复杂度比较大”,并说他“第一眼看到我就想把它当成一个数学问题来做”之后 ...

  2. C语言初学者代码中的常见错误与瑕疵(3)

    问题: n-1位数字 已知w是一个大于10但不大于1000000的无符号整数,若w是n(n≥2)位的整数,则求出w的后n-1位的数. 输入: 第一行为M,表示测试数据组数. 接下来M行,每行包含一个测 ...

  3. K-means中的K值选择

    关于如何选择Kmeans等聚类算法中的聚类中心个数,主要有以下方法(译自维基): 1. 最简单的方法:K≍sqrt(N/2) 2. 拐点法:把聚类结果的F-test值(类间Variance和全局Var ...

  4. php sqrt()函数 语法

    php sqrt()函数 语法 作用:sqrt()函数的作用是对参数进行求平方根 语法:sqrt(X) 参数: 参数 描述 X 进行求平方根的数字 说明:返回将参数X进行开平方后的结果江苏大理石平台 ...

  5. Wannafly Camp 2020 Day 1I K小数查询 - 分块

    给你一个长度为\(n\)序列\(A\),有\(m\)个操作,操作分为两种: 输入\(x,y,c\),表示对\(i\in[x,y]\),令\(A_{i}=min(A_{i},c)\) 输入\(x,y,k ...

  6. 密码学笔记(5)——Rabin密码体制和语义安全性

    一.Rabin密码体制 Rabin密码体制是RSA密码体制的一种,假定模数$n=pq$不能被分解,该类体制对于选择明文攻击是计算安全的.因此,Rabin密码体制提供了一个可证明安全的密码体制的例子:假 ...

  7. UOJ #221 【NOI2016】 循环之美

    题目链接:循环之美 这道题感觉非常优美--能有一个这么优美的题面和较高的思维难度真的不容易-- 为了表示方便,让我先讲一下两个符号.\([a]\)表示如果\(a\)为真,那么返回\(1\),否则返回\ ...

  8. Sicily 1444: Prime Path(BFS)

    题意为给出两个四位素数A.B,每次只能对A的某一位数字进行修改,使它成为另一个四位的素数,问最少经过多少操作,能使A变到B.可以直接进行BFS搜索 #include<bits/stdc++.h& ...

  9. HDU 5754 Life Winner Bo 组合博弈

    Life Winner Bo Problem Description   Bo is a "Life Winner".He likes playing chessboard gam ...

随机推荐

  1. Top 10 Free Wireless Network hacking/monitoring tools for ethical hackers and businesses

    There are lots of free tools available online to get easy access to the WiFi networks intended to he ...

  2. Magento white screen or how XML can break your site?

    Magento white screen or how XML can break your site? by SANDO on 02. OCT, 2012 in MAGENTO, SMALL TIP ...

  3. asp:gridview 中显示日期格式

    boundfield中应该这样设置: <asp:BoundField HeaderText="发表时间" DataField="PostTime" Htm ...

  4. leetcode算法

    Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull. Follo ...

  5. js - 驼峰命名

    1. // 驼峰命名 console.log(hump('border-bottom-color')) function hump( str) { if (typeof str != 'string' ...

  6. 关于使用"/"来 dispatcherServlet 的url-pattern带来的问题

    之前一直使用*.do来做的,但是绝的*.do很丑,于是就改用"/"来配置: <servlet> <servlet-name>dispatcherServle ...

  7. java获取当前时间戳的方法

    获取当前时间戳 //方法 一 System.currentTimeMillis(); //方法 二 Calendar.getInstance().getTimeInMillis(); //方法 三 n ...

  8. android点滴之ViewTreeObserver

    一类的基本概念 这是一个注册监听视图树的观察者(observer),在视图树种全局事件改变时得到通知.这个全局事件不仅还包括整个树的布局,从绘画过程开始,触摸模式的改变等.最常见的用途时通过监听获知什 ...

  9. C# GridControl 行背景颜色

    使用C# DevExpress_gridControl 行号行样式显示行背景颜色,必须取消自动变换行色属性 取消Focus变色,属性为OptionSelection--EnableAppearance ...

  10. Java 收集的代码 transient

    public class Main { public static void main(String[] args) { ((NULL)null).haha(); } } class NULL { p ...