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编码优势的地方。

posted @ 2013-02-05 16:08  技术笔记2012  阅读(4296)  评论(15编辑  收藏  举报