Kaprekar constant(卡普雷卡尔黑洞)
昨天在朋友的微博里看到一条关于数字迭代的有趣的题目。然后正好自己刚刚放假就没事写写,正好检验下我最近算法是否提高,其中弯路很多,追求在多次实践中来锻炼自己的逻辑和编码能力。
其中描述是:
把一个四位数的四个数字由小至大排列,组成一个新数,又由大至小排列排列组成一个新数,这两个数相减,之后重复这个步骤,只要四位数的四个数字不重复,数字最终便会变成 6174。
例如:
3109,
9310 - 0139 = 9171,
9711 - 1179 = 8532,
8532 - 2358 = 6174。
而 6174 这个数也会变成 6174,7641 - 1467 = 6174。
任取一个四位数,只要四个数字不全相同,按数字递减顺序排列,构成最大数作为被减数;按数字递增顺序排列,构成最小数作为减数,其差就会得6174;如不是6174,则按上述方法再作减法,至多不过10步就必然得到6174。
但是其中需要8步的初始数,我还没找(我把99999个随机数所转换数据导入到txt中,大小为8M,也没找到)
一个7步的示例:
如取四位数5679,按以上方法作运算如下:
9765-5679=4086 8640-4068=4572 7542-2457=5085
8550-5058=3492 9432-2349=7083 8730-3078=5652
6552-2556=3996 9963-3699=6264 6642-2466=4176
7641-1467=6174
其中c#实现代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace ConsoleApplication3 { class Program { static int[] rnd; static int N = 0; const int result = 6174; static Random rn = new Random(); static StringBuilder output = new StringBuilder(); static void Main(string[] args) { for (int i = 0; i < 99999; i++) { rndArr(4); output.AppendLine("初始数为:" + arrToint(rnd).ToString()); output.Append("差值过程数分别为:"); if (function(rnd)) { output.AppendLine("执行次数为" + N.ToString() + "次"); output.AppendLine(""); N = 0; } } try { string filepath = "d:/1.txt"; FileStream fs = new FileStream(filepath, FileMode.Create); StreamWriter sw = new StreamWriter(fs); sw.Write(output); sw.Close(); Console.WriteLine("success!"); Console.WriteLine("已经写到" + filepath); } catch { Console.Write("0"); } Console.ReadKey(); } /// <summary> /// 迭代求计算次数 /// </summary> /// <param name="rTemp">差值循环</param> /// <returns>找到固定值返回True</returns> static bool function(int[] rTemp) { cba(rnd); //整理成可转换成最大值的int[] int newint = arrToint(rTemp) - arrTointR(rTemp); N++; output.Append(newint + ","); if (newint == result) { return true; } else return function(cba(intToarr(newint))); } /// <summary> /// 数组转换成数值(确保参数为最大值) /// </summary> /// <param name="a"></param> /// <returns>最大值</returns> static int arrToint(int[] a) { string temp = ""; for (int i = 0; i < a.Length; i++) temp += a[i].ToString(); return Int16.Parse(temp); } /// <summary> /// 将数组反向再转换成数值(确保参数为最大值) /// </summary> /// <param name="a"></param> /// <returns>最小值</returns> static int arrTointR(int[] a) { string temp = ""; for (int i = 0; i < a.Length; i++) temp += a[a.Length - i - 1].ToString(); return Int16.Parse(temp); } /// <summary> /// int值转成数组 /// </summary> /// <param name="a"></param> /// <returns></returns> static int[] intToarr(int a) { int length = a.ToString().Length; int[] re = new int[length]; for (int i = 0; i < length; i++) { re[i] = a / Convert.ToInt16(Math.Pow(10, length - 1 - i)); a = a - re[i] * Convert.ToInt16(Math.Pow(10, length - 1 - i)); } return re; } /// <summary> /// 产生初始随机数组 /// </summary> /// <param name="length"></param> static void rndArr(int length) { rnd = new int[length]; int[] rndtemp = new int[rnd.Length]; for (int i = 0; i < length; i++) { do { rndtemp[i] = rn.Next(10); } while (validate(rndtemp, rndtemp[i], i) || rndtemp[0] == 0); } rnd = rndtemp; } /// <summary> /// Random Validate 如果存在返回true /// </summary> /// <param name="v"></param> /// <param name="x">当前序号标记</param> /// <returns></returns> static bool validate(int[] temp, int v, int x) { int k = 0; for (int i = 0; i < x; i++) { if (temp[i] == v) k++; } if (k > 0) return true; return false; } /// <summary> /// 随机数组转换成最大值数组 /// </summary> /// <param name="temp"></param> /// <returns></returns> static int[] cba(int[] temp) { for (int i = 0; i < temp.Length - 1; i++) { for (int j = 0; j < temp.Length - i - 1; j++) { if (temp[j] < temp[j + 1]) { temp[j] = temp[j] + temp[j + 1]; temp[j + 1] = temp[j] - temp[j + 1]; temp[j] = temp[j] - temp[j + 1]; } } } return temp; } } }
其中用到了迭代算法,这算是第一次尝试,路漫漫,其中可能有一定多绕步,过路人可以指出,在下在这感谢各位。
参考资料:http://en.wikipedia.org/wiki/D._R._Kaprekar#cite_note-4
下面是网友给出的利用Linq写的算法:
static void Main(string[] args) { Process(1234, 1); Console.ReadKey(); } private static void Process(int number, int deep) { var numbers = new int[] { number % 10, (number % 100) / 10, (number % 1000) / 100, number / 1000 }; var ascOrder = numbers.OrderBy(n => n).ToArray(); var descOrder = numbers.OrderByDescending(n => n).ToArray(); var weiQuan = 10000; var smaller = ascOrder.Select(a => { weiQuan = weiQuan / 10; return a * weiQuan; }).Sum(); weiQuan = 10000; var bigger = descOrder.Select(a => { weiQuan = weiQuan / 10; return a * weiQuan; }).Sum(); var diff = bigger - smaller; Console.WriteLine(string.Format("第{4}次:{0}:{1}-{2}={3}", number, bigger, smaller, diff, deep)); if (diff == number) { Console.WriteLine("That's just right"); return; } else if (deep >= 10) { Console.WriteLine("计算次数超过10次,尚未证明结果,退出"); return; } else { Process(diff, ++deep); } }
首先我在随机4个数里面写了算法,然后在排序里面也相对写复杂了,网友这算法正是突显Linq编码优势的地方。