一个很有趣的程序员等级考试题----求循环小数

前几天公司公司一直有程序员面试,经理出了这样一道面试题:求两个数相除的商,并计算出小数点后的循环部分。例如1/3 商为0.333333333... ,则表示为0.(3)

当时,我也没见过这样的题,呵呵!实在惭愧!于是,下班回到家用asp.net简单实现以下:

aspx页:

代码
1 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default5.aspx.cs" Inherits="Default5" %>
2
3  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
5  <html xmlns="http://www.w3.org/1999/xhtml">
6  <head runat="server">
7 <title></title>
8  </head>
9  <body>
10 <form id="form1" runat="server">
11 <div>
12 <p>
13 除数:<asp:TextBox ID="txtDivisor" runat="server" onkeyup="if(event.keyCode!=37&amp;&amp;event.keyCode!=39)value=value.replace(/\D/g,'');"></asp:TextBox>
14 </p>
15 <p>
16 被除数:<asp:TextBox ID="txtDividend" runat="server" onkeyup="if(event.keyCode!=37&amp;&amp;event.keyCode!=39)value=value.replace(/\D/g,'');"></asp:TextBox>
17 </p>
18 <p>
19 小数个数:<asp:TextBox ID="txtCount" runat="server" onkeyup="if(event.keyCode!=37&amp;&amp;event.keyCode!=39)value=value.replace(/\D/g,'');"></asp:TextBox>
20 </p>
21 <asp:Button ID="btnResults" runat="server" Text="结果"
22 onclick="btnResults_Click" />
23 <p>
24 <asp:Label ID="lbResults" runat="server" Text=""></asp:Label>
25 </p>
26 </div>
27 </form>
28  </body>
29  </html>

 

 

aspx.cs页:

代码
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.UI;
6 using System.Web.UI.WebControls;
7
8 public partial class Default5 : System.Web.UI.Page
9 {
10 protected void Page_Load(object sender, EventArgs e)
11 {
12 if (IsPostBack)
13 {
14 txtDivisor.Text = txtDivisor.Text;
15 txtDividend.Text = txtDividend.Text;
16 txtCount.Text = txtCount.Text;
17 }
18 }
19 protected void btnResults_Click(object sender, EventArgs e)
20 {
21 if (txtDivisor.Text.Trim().Length < 1)
22 {
23 return;
24 }
25 if (txtDividend.Text.Trim().Length < 1)
26 {
27 return;
28 }
29 if (txtCount.Text.Trim().Length < 1)
30 {
31 return;
32 }
33
34 int _divisor = Convert.ToInt32(txtDivisor.Text);
35 int _dividend = Convert.ToInt32(txtDividend.Text);
36 int _count = Convert.ToInt32(txtCount.Text);
37 if (_dividend == 0)
38 {
39 return;
40 }
41 lbResults.Text = DivResult(_divisor, _dividend, _count);
42
43 }
44 //运算主体
45 protected string DivResult(int divisor, int dividend, int count)
46 {
47 string _divresult = "";
48 int tempdivisor = 0;
49 for (int i = 0; i <= count; i++)
50 {
51 if (i == 0)
52 {
53 _divresult = (divisor / dividend).ToString() + ".";
54 tempdivisor = divisor % dividend;
55 }
56 else
57 {
58 int _tempdivisor = MaxMin(tempdivisor, dividend);
59 if (_tempdivisor != 0)
60 {
61 _divresult = _divresult + ReturnZero(_tempdivisor, tempdivisor) + (_tempdivisor / dividend).ToString();
62 tempdivisor = _tempdivisor % dividend;
63 }
64 else
65 {
66 _divresult = _divresult + '0';
67 }
68 }
69 }
70 string[] temp = _divresult.Split('.');
71 string temp1;
72 temp1= temp[1] = temp[1].Length < count ? temp[1] : temp[1].Substring(0, count);
73 string _repeatstr = CheckRepeat(temp[1]);
74 if (_repeatstr.Length > 0)
75 {
76 temp[1] = temp[1].Remove(temp[1].IndexOf(_repeatstr)) + "(" + _repeatstr + ")";
77 }
78
79 return temp[0] + "." + temp[1] + "<br/>" + temp[0] + "." + temp1;
80 }
81 //判断返回的零
82 private string ReturnZero(int _tempdivisor, int tempdivisor)
83 {
84 int n = (int)Math.Log10(_tempdivisor / tempdivisor);
85 string ret = "";
86 for (int i = 1; i < n; i++)
87 {
88 ret += '0';
89 }
90 return ret;
91 }
92 //判断除数与被除数的大小
93 private int MaxMin(int divisor, int dividend)
94 {
95 if (divisor > dividend && divisor != 0)
96 {
97 return divisor;
98 }
99 else if (divisor == 0)
100 {
101 return divisor;
102 }
103 else
104 {
105 return MaxMin(divisor * 10, dividend);
106 }
107 }
108 //判断小数点后的循环部分
109 private string CheckRepeat(string oldstr)
110 {
111 int[] olditems = new int[oldstr.Length];
112 for (int i = 0; i < oldstr.Length; i++)
113 {
114 olditems[i] = Convert.ToInt32(oldstr.Substring(i, 1));
115 }
116
117 int n = 1; //每次比较的次数
118 int equalcount = 0;//每次比较相等的次数
119 for (int i = olditems.Length - 1; i > olditems.Length / 2; i--)
120 {
121 equalcount = 0;
122 for (int j = 0; j < n; j++)
123 {
124 if (olditems[olditems.Length - 1 - j] != olditems[olditems.Length - 1 - j - n])
125 {
126 break;
127 }
128 equalcount++;
129 }
130 if (n == equalcount)
131 {
132 break;
133 }
134 n++;
135 }
136 string ret = "";
137 for (int i = equalcount - 1; i >= 0; i--)
138 {
139 ret += olditems[olditems.Length - 1 - i].ToString();
140 }
141 return ret;
142 }
143 }
144

 

 

我只是简单的实现了一下!没有专门测试!如果有bug,请园友发消息告诉我啊!! 呵呵..

 

 

标签: C#, 循环小数
posted @ 2010-03-19 10:07 凉风入夜 阅读(2384) 评论(34) 编辑 收藏

 回复 引用 查看   
#1楼2010-03-19 10:42 | 永远的雨林      
有兴趣
 回复 引用 查看   
#2楼2010-03-19 11:02 | Cat Chen      
为什么需要用户输入小数个数?用户怎么可能知道小数个数?这用小学的除法运算方式来做就可以了,不停地做一位除法,直到余数为零,或者余数重复出现为止。
 回复 引用 查看   
#3楼[楼主]2010-03-19 11:07 | 凉风入夜      
@Cat Chen
因为有些小数是无限不循环小数(像圆周率∏),如果不输入小数位数,会进入死循环! 如果单纯的用小学的除法运算,会出现有些循环小数取不到!因为c#中int类型的数据长度有限!如果用string类型存储数据,效果会更好!

 回复 引用 查看   
#4楼2010-03-19 11:28 | 小猴子      
@Cat Chen
支持看法。LZ只是用程序来验证自己的结果,不是用来解题的。

 回复 引用 查看   
#5楼2010-03-19 11:40 | AutumnWinter      
凑个热闹,C++代码,比较乱
思路:
两个数组,一个存储商,一个存储余数,前者用于检查余数是否重复出现,后者用来输出小数部分。

1. 能整除的直接输出
2. 否则,一直除,直到余数重复出现。输出所有的小数部分即可,两次余数出现之间的加上括号

代码:
#include <iostream>
using namespace std ;

// 检查余数curValue是否在以前出现过,以前的余数保存在数组a中
int Check(int a[], int maxIndex, int curValue)
{
	for (int i = 0; i < maxIndex ; i++)
	{
		if(a[i] == curValue)
			return i ;
	}
	return -1 ;
}

// 输出a / b,假设b不为0
// i是存放商的数组,f是存放余数部分的数组
void Fraction(int i[], int f[], int a, int b)
{
	// 能整除
	if(a % b == 0)
	{
		cout << a / b << endl ;
		return ;
	}

	// 输出整数部分
	cout << a / b << "." ;

	int c = 0 ; // 数组下标
	int t = a % b ;
	i[c] = t ;	// 第一个余数
	while (true)
	{
		if (t < b)
			t *= 10 ;		// 余数乘10,进行下一次
		f[c] = t / b ;		// 存储商
		i[++c] = t % b ;	// 存储余数
		t %= b ;			// 除

		// 检查余数是否在之前出现过
		int r = Check(i, c, t % b) ;

		// 如果本次余数之前出现过,表明循环开始了
		if(r != -1)
		{
			// 输出常规小数部分
			for (int j = 0; j < r; j++)
				cout << f[j] ;

			cout << "(" ;

			// 输出循环小数部分
			for (int j = r; j < c; j++)
				cout << f[j] ;

			break ;
		}
	}

	cout << ")" << endl ;
}

int main(void)
{
	const int n = 100 ;
	int i[n] ; // 存储商的数组
	int f[n] ; // 存储余数的数组

	Fraction(i, f, 1, 3) ;

	system("pause") ;
	return 0 ;
}

 回复 引用 查看   
#6楼2010-03-19 11:42 | Ivony...      
引用凉风入夜:
@Cat Chen
因为有些小数是无限不循环小数(像圆周率∏),如果不输入小数位数,会进入死循环! 如果单纯的用小学的除法运算,会出现有些循环小数取不到!因为c#中int类型的数据长度有限!如果用string类型存储数据,效果会更好!



所有的无限不循环小数都不能表示为两个整数之商,因为它们是无理数。。。。。


传说在两千多年前毕达哥拉斯的弟子希伯斯发现了无理数,结果被杀了,结果几千年后的LZ竟然会说无理数可以由两个整数相除除出来,希伯斯真是死的太冤枉了。。。。

 回复 引用 查看   
#7楼2010-03-19 11:44 | Greatest      
引用凉风入夜:
@Cat Chen
因为有些小数是无限不循环小数(像圆周率∏),如果不输入小数位数,会进入死循环! 如果单纯的用小学的除法运算,会出现有些循环小数取不到!因为c#中int类型的数据长度有限!如果用string类型存储数据,效果会更好!

看来lz的数学水平连小学生都不如。。。。。
小学生都知道,2个数(有理数)相除的结果不可能是无理数的。

 回复 引用 查看   
#8楼[楼主]2010-03-19 11:50 | 凉风入夜      
@Greatest
呵呵 这些纯数学的东西有时候用程序是说不清的!比如 1/3的商,用数学的思想就是0.3... 但是用单纯的用程序实现时结果就是0。
我只是想用程序的思想实现这种求循环小数的循环部分!其他的不在我的考虑范围!

 回复 引用 查看   
#9楼2010-03-19 11:51 | 不得闲      
哈哈,也来热闹一下!Delphi的代码!
我的这个啥都没用,就是循环比对余数与被除数是否相同(递归比较),相同了再比对一次,如果还相同,就直接返回除数的结果了!呵呵,欢迎指正
var
  RunCount: Integer;

function TestDiv(x,y: Integer): Integer;
var
  t,t1,a,b: Integer;
begin
  Result := 0;
  if y = 0 then
  begin
    RunCount := 0;
    exit;
  end;
  while x < y do
    x := x* 10;
  a := x;
  b := y;
  Inc(RunCount);
  if RunCount > 10 then
  begin
    ShowMessage('无有效的循环数据');
    RunCount := 0;
    exit;
  end;
  //先求最大公约数
  t := y;
  if x < y then
  begin
    y := x;
    x := t;
  end;
  while t > 0 do
  begin
    t := x mod y;
    x := y;
    if t > 0 then y := t;
  end;
  x := a div y; //约去约数
  y := b div y;

  x := x mod y;
  while x <> 0 do
  begin
    t := x;
    if x = 1 then
    begin
      Result := TestDiv(x,y);
      RunCount := 0;
      exit;
    end;
    t1 := x;
    while t1 < y do
      t1 := t1* 10;
    x := t1 mod y;
    if t = x then
    begin
      while x < y do
        x := x* 10;
      if x mod y = 0 then
        Result := 0
      else
      begin
        Result := x div y;
      end;
      RunCount := 0;
      exit;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(TestDiv(StrToInt(edit1.Text),StrToInt(Edit2.Text))));
end;

 回复 引用 查看   
#10楼2010-03-19 13:28 | Cat Chen      
@凉风入夜
除法不会出现无限不循环小数。如果已经忘记了有理数和无理数的区别,请回去找初中数学课本看看。

 回复 引用 查看   
#11楼2010-03-19 13:54 | liy-apple      
你怎么不用"1/3.0",然后保留你需要的小数位数。
何必搞这么麻烦

 回复 引用 查看   
#12楼[楼主]2010-03-19 13:57 | 凉风入夜      
@liy-apple
用数值类型保存有局限性!

 回复 引用 查看   
#13楼2010-03-19 14:04 | liy-apple      
有什么局限性?
 回复 引用 查看   
#14楼2010-03-19 14:09 | liy-apple      
你用String存了,你总归是要转到数字类型上去的。
如果不是大数,有那个必要吗?

 回复 引用 查看   
#15楼[楼主]2010-03-19 14:13 | 凉风入夜      
@liy-apple
就是考虑到一个很小的整数(指正整数)除以一个很大的整数!这样的话用数字类型保存就有局限性了

 回复 引用 查看   
#16楼2010-03-19 14:21 | Cheese      
"我只是想用程序的思想实现这种求循环小数的循环部分" 按照楼主的想法,
其实这个题主要就是想考察: 求一个数组循环出现的子集
比如{2,3,1,4,2,2,3,1,4,2,2,3,1,4,2} 那么结果就是 {2,3,1,4,2}


 回复 引用 查看   
#17楼[楼主]2010-03-19 14:25 | 凉风入夜      
@Cheese
可以这样认为!在程序中,我把这部分写在了判断循环部分的函数中,其实,这是一道程序员等级考试题,应该是用C++实现的!我只是想用c#实现一下自己的思路!

 回复 引用 查看   
#18楼2010-03-19 14:29 | 李晓强      
@凉风入夜
@Cat Chen
因为有些小数是无限不循环小数(像圆周率∏),如果不输入小数位数,会进入死循环! 如果单纯的用小学的除法运算,会出现有些循环小数取不到!因为c#中int类型的数据长度有限!如果用string类型存储数据,效果会更好!
-------------------------------------------------
楼主啊,说你什么好呢?
莫非你不知道,啥叫有理数?

 回复 引用 查看   
#19楼[楼主]2010-03-19 14:54 | 凉风入夜      
@李晓强
呵呵 现在大家一直拿这个问题来问我!其实,上面的题目就是求两个int类型的商,并计算出小数点后的循环部分!就是这样一道题!我只想用C#实现一下自己的思路!其它的什么有理数,无理数等等啦!单纯的数学理论没有考虑进去!呵呵!如果拿单纯的数学理论,然后用程序实现,应该很困难吧!1/3应该等于0.3... 但是用程序实现就是0了(在不考虑double类型的情况下,因为纯数学理论中应该没有double类型吧!!)!

 回复 引用 查看   
#20楼2010-03-19 15:20 | 走在IT路上的蜗牛      
不大明白为什么要加上ReturnZero这个函数。
 回复 引用 查看   
#21楼[楼主]2010-03-19 15:29 | 凉风入夜      
@走在IT路上的蜗牛
因为在除法中求小数点后的数字时,余数要扩大十倍后再与被除数相除,当扩大十倍后余数仍小于被除数时,应再扩大十倍后与倍除数相除,这个时候应在小数位上加一个零!依次类推!

 回复 引用 查看   
#22楼2010-03-19 15:48 | 走在IT路上的蜗牛      
@凉风入夜
哦,那这个函数好像有点错误哟。
int n = _tempdivisor / tempdivisor / 10;
string ret = "";
for (int i = 1; i < n; i++)

如果_tempdivisor等于100
tempdivisor 等于1
应该是多加了2个0,
但是你这样除的话,n=10.
结果就循环加了9个0哦。

 回复 引用 查看   
#23楼[楼主]2010-03-19 15:56 | 凉风入夜      
@走在IT路上的蜗牛
恩,是!这个地方写错了! 你能改一下,发给我吗??

 回复 引用 查看   
#24楼2010-03-19 16:37 | 杨圣青      
楼主写得为什么会这么复杂?

    Protected Function div(ByVal a As Integer, ByVal b As Integer) As String
        Dim s As String = ""

        If a < b Then
            s = "0."
            a = a * 10
        Else
            s = Int(a / b).ToString() & "."
            a = a - Int(a / b) * b
            a = a * 10
        End If

        Dim l As New List(Of Integer)

        While Not l.Contains(a)
            If Int(a / b) = 0 Then
                a = a * 10
                s &= "0"
            Else
                l.Add(a)
                s &= Int(a / b).ToString()
                a = a - Int(a / b) * b
                a = a * 10
                If a = 0 Then
                    Return s
                End If
            End If

        End While

        For i As Integer = 0 To l.Count - 1
            If a = l(i) Then
                s = s.Insert(i + 2, "[")
            End If
        Next

        Return s & "]"

    End Function

 回复 引用 查看   
#25楼2010-03-19 17:35 | 走在IT路上的蜗牛      
@凉风入夜
我认为把那个n改成商的长度就是要添加几个0的位数
int n =(_tempdivisor / tempdivisor / 10).ToString().Length;

 回复 引用 查看   
#26楼[楼主]2010-03-19 17:37 | 凉风入夜      
@走在IT路上的蜗牛
我已经改过了!用的函数是Math.Log10()。

 回复 引用 查看   
#27楼2010-03-19 17:41 | 走在IT路上的蜗牛      
嗯。
这个更好。

 回复 引用 查看   
#28楼2010-03-19 20:47 | Henllyee Cui      
其实这个题目,我们上学的时候学C++的时候,就做过,呵呵
 回复 引用 查看   
#29楼2010-03-19 21:49 | duncannjm      
这明显是考智力啊!
 回复 引用 查看   
#30楼2010-03-25 11:45 | iblogger      
这种事关小数点的计算操作,参数int就肯定有BUG。

比如用83/26,程序打印出的结果是
3.01(090203000706)
3.01090203000706090203000706090203000706090203000706
而用计算器计算可知,83/26的结果应该是
3.1923076923076923076923076923
最后将接收参数类型改成decimal就可以了。

 回复 引用 查看   
#31楼[楼主]2010-03-25 12:03 | 凉风入夜      
@iblogger
这位朋友,你可能还没有把我写的代码粘贴复制,然后自己亲自运行一下!请你试一试,就知道了!!

 回复 引用 查看   
#32楼[楼主]2010-03-25 12:07 | 凉风入夜      
@iblogger
这是我的运行结果 截图:

 回复 引用 查看   
#33楼[楼主]2010-03-25 12:58 | 凉风入夜      
@iblogger
呵呵 这就不清楚了 你再重新粘贴复制一下 试试

 回复 引用   
#34楼2011-03-28 09:26 | nbxwj[未注册用户]
小学生用free pascal编的
var
a,b,n,t,i,st,len:longint; flag:boolean;stb:string;
c:array[1..10000] of longint;
begin
readln(a,b,n); str(b,stb);len:=length(stb);
write(a div b);
a:=a mod b;
if a=0 then begin writeln;exit;end;
write('.');t:=0;flag:=false;
repeat
inc(t);c[t]:=a*10 div b;
a:=a*10 mod b;if a=0 then break;
for i:=1 to t-1 do
if (c[i]=c[t])and (t-i>=len) then begin
flag:=true;st:=i;break;
end;
until (a=0) or (t>=n) or flag;
if not flag then begin
for i:=1 to t do write(c[i]);writeln;
end else begin
for i:=1 to st-1 do write(c[i]);
write('(');
for i:=st to t-1 do write(c[i]);
writeln(')');
end;
end.

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1689582 VEkZmBBh4w4=