Google方程式 WWWDOT – GOOGLE = DOTCOM
发布时间:2016-09-18, 17:47:29 分类:Linux | 编辑 off 网址 | 辅助
图集1/3
正文 11496字数 1,384,865阅读
有一个字符组成的等式:WWWDOT – GOOGLE = DOTCOM,每个字符代表一个0-9之间的数字,WWWDOT、GOOGLE和DOTCOM都是合法的数字,不能以0开头。请找出一组字符和数字的对应关系,使它们互相替换,并且替换后的数字能够满足等式。这个字符等式是Google公司能力倾向测试实验室的一道题目,这种题目主要考察人的逻辑推导能力和短期记忆能力,通常棋下的好的人解决这类问题会更得心应手一些(飞行棋例外)。此类型的题目有很多变种,各种编程比赛中常常能见到它们的身影。比如2005年的GOOGLE中国编程挑战赛第二轮淘汰赛有一道名为“SecretSum”的500分的竞赛题,与本题如出一辙,只不过字母都是三个,而且用的是加法计算。现在言归正传,先看看如何分析这个问题。以人的思维方式分析问题
将横式改成竖式可能更直观一些:
根据以上竖式减法,从左向右依次可以得到6个算式,分别是:
W – G = D (算式 1)
W – O = O (算式 2)
W – O = T (算式 3)
D – G = C (算式 4)
O – L = O (算式 5)
T – E = M (算式 6)
根据以上6个算式可以分析出两个关键信息:一个是W要足够大,因为考虑到它可能被借位的情况还要等于G和D的和;另一个则是本问题的突破口,就是算式2和算式3两次出现的W – O计算。现在分析算式2和算式3,根据是否需要借位,算式2和算式3一共有四种借位组合结果,下面分别对这四种借位组合结果进行分析。
1. W – O = T不需要借位,W – O = O也不需要借位
由于W – O = T和W – O = O都不需要借位,则可由算式2变形得到算式1.1:
W = 2O (算式1.1)
将算式1.1带入算式3,又可以得到算式1.2:
O = T (算式 1.2)
根据算式1.2,O和T代表的数字是同一个数字,这与题目要求不符,因此,这种借位组合不能得到正确的结果。
2. W – O = T需要借位,W – O = O不需要借位
根据借位情况,对算式2和算式3进行借位修正,得到两个修正算式:
W – 1 – O = O (算式2.1)
W + 10 – O = T (算式2.2)
由算式2.1变形得到算式2.3:
W = 2O + 1 (算式2.3)
将算式2.3带入算式2.2可以得到算式2.4:
T = O + 11 (算式2.4)
对算式2.3分析,由于W是个位数,最大值是9,所以O的取值只能是1-4,但是无论如何,由算式2.4计算出的T都超过9,这与题目要求不符,因此,这种情况也是无解的情况。
3. W – O = T不需要借位,W – O = O需要借位
根据借位情况,对算式2和算式3进行借位修正,得到两个修正算式:
W + 10 – O = O (算式3.1)
W – O = T (算式3.2)
由算式3.1变形得到算式3.3:
W = 2O – 10 (算式3.3)
将算式3.3带入算式3.2由可得到算式3.4:
O – 10 = T (算式3.4)
O显然是不能比10大的个位数,因此,这种情况也是无解的情况
4. W – O = T需要借位,W – O = O也需要借位
根据借位情况,对算式2和算式3进行借位修正,得到两个修正算式:
W – 1 + 10 – O = O (算式4.1)
W + 10 – O = T (算式4.2)
由算式4.1变形得到算式4.3:
W = 2O – 9 (算式4.3)
将算式4.3代入算式4.2得到算式4.4:
O + 1 = T (算式4.4)
由于W不能小于0,因此,根据算式4.3,O的取值最小为5。根据算式4.4继续分析,因为T不能大于9,因此O的最大值只能取值为8。根据O的取值区间[5,8],可依次计算出W和T的值,如下表所示:
已知O、W、T的取值,可以进一步推算其他字符代表的数字,上表中得到了四组目前合法的取值,但是并不是四组取值都能最终推算出正确的结果,本题的答案只有一个,也就是说只有一组O、W、T的取值是正确的,下面就分别进行分析。
O = 5, W = 1, T = 7
在这种情况下,考察算式1:W – G = D,W = 1显然无法满足此种情况,更何况算式2: W – O = O还要从它这里借位,因此,这种情况无解。
O = 6, W = 3, T = 7
在这种情况下,算式2: W – O = O还要从它这里借位,因此算式1:W – G = D对应的实际情况是2 – G = D,G和D不能同时为1,而且G和D都是第一位数字,不能是0,因此无法满足算式1,这种情况也是无解。
O = 7, W = 5, T = 8
在这种情况下,需要考察另另外两个关键算式,分别是算式5和算式6。根据这两个算式是否需要借位进行不同的假设,根据组合,仍然有四种假设,下面分别分析这四种假设:
假设一:算式5需要借位,算式6不需要借位。则此时算式5可修正为O + 10 – L = O,推算出L = 10,显然不符合题意,假设一不成立;
假设二:算式5需要借位,算式6需要借位,则算式5和算式6应该修正为算式4.3.1和算式4.3.2:
O -1 + 10 – L = O (算式4.3.1)
T + 10 – E = M (算式4.3.2)
因为已知T=8,带入4.3.2可得E+M=18,显然对于两个不相同的个位数无法满足这个等式,因此假设二也不成立;
假设三:算式5不需要借位,算式6不需要借位,此时根据算式6可知E和M的和是8(T=8),排除E=M=4的情况后,E和M的组合可以是(1,7)、(2,6)和(3,5),又因为数字5和7分别被W和O使用,因此E和M只能是2或6。再回头来看算式1,因为算式2需要借位,算式1实际相当于G + D = 4,G和D只能取值1和3,若G=1,D=3,则根据算式4计算出C=2,这与E或M矛盾。若G=3,D=1,则算式4需要借位,这又与算式3的假设矛盾。由此看来,假设三也不能得到正确的结果;
假设四:算式5不需要借位,算式6需要借位,此时根据算式5被修正为O – 1 – L = O,这种情况下也是无解的。
O = 8, W = 7, T = 9
在这种情况下,根据算式5和算式6是否借位的假设,仍然有四种假设,下面分别分析这四种假设:
假设一:算式5需要借位,算式6不需要借位。则此时算式5可修正为O + 10 – L = O,推算出L = 10,显然不符合题意,假设一不成立;
假设二:算式5需要借位,算式6需要借位,则算式5和算式6应该修正为算式4.4.1和算式4.4.2:
O -1 + 10 – L = O (算式4.4.1)
T + 10 – E = M (算式4.4.2)
因为已知T=9,带入4.4.2可得E+M=19,两个不同的个位数的和不可能大于18,因此假设二也不成立;
假设三:算式5不需要借位,算式6不需要借位,此时根据算式6可知E和M的和是9(T=9),E和M的组合可以是(1,8)、(2,7) 、(4,5)和(3,6),又因为数字8和7分别被O和W使用,因此E和M只能是(4,5)和(3,6)。进一步假设E=4,M=5(反过来E=5,M=4是一样的,不影响分析)。再看算式1,因为算式2需要借位,算式1实际相当于G + D = 6,由于M或E是5,所以G和D只能取值2和4。若G=2,D=4,则根据算式4计算出C=2,这与G=2矛盾。若G=4,D=2,则算式4需要借位,这又与算式3的假设矛盾,因此E=4,M=5的情况无解。再次进一步假设E=3,M=6(反过来E=6,M=3是一样的,不影响分析)。同样再看算式1,G和D的值可取是(2,4)和(1,5),G和D取值1和2的情况刚刚分析过无解,因此G和D的取值只能是1和5,前面分析过,算式4没有借位,也就是说要保证D > G,因此,D=5,G=1,根据算式4计算出C=4,这样就得到了一组解:O = 8,W = 7,T = 9,D = 5,L = 0, G = 1,C = 4,E = 3/6,M = 6/3。最终的等式是:
777589 – 188103 = 589486
或
777589 – 188106 = 589483
假设四:算式5不需要借位,算式6需要借位,此时根据算式5被修正为O – 1 – L = O,这种情况下也是无解的。
完整的分析过程结束,得到了一组答案,事实上通过计算机穷举算法也只能得到这一组结果,下面就看看如何用计算机算法求解本题的答案
用计算机穷举所有的解
以上是用人的思维方式的解题过程,如果方法正确,加上运气好(三次假设都是正确的,避免在错误分支上浪费时间),两分钟内就可得到结果。但是考虑到更通用的情况,字母数字没有规律,也没有可供分析的入手点和线索,比如:
AAB – BBC = CCD
这样的问题,该什么方法解决呢?只能“猜想”,用穷举的方法试探每一种猜想,对每个字母和数字穷举所有可能的组合,直到得到正确的结果。当然,这样的力气活交给计算机做是最合适不过了。
1. 建立数学模型
要想让计算机解决问题,就要让计算机能够理解题目,这就需要建立一个计算机能够识别、处理的数学模型,首先要解决的问题就是建立字母和数字的映射关系的数学模型。本题的数学模型很简单,就是一个字母二元组:{char, number}。考察等式:
WWWDOT – GOOGLE = DOTCOM
共出现了9个不同的字母:W、D、O、T、G、L、E、C和M,因此,最终的解应该是9个字母对应的字母二元组向量:[ {‘W’, 7}, {‘D’, 5}, {‘O’, 8}, {‘T’, 9}, {‘G’, 1}, {‘L’, 0}, {‘E’, 3}, {‘C’, 4}, {‘M’, 6} ]。穷举算法就是对这个字母二元组向量中每个字母二元组的number元素进行穷举,number的穷举范围就是0-9共10个数字,当然,根据题目要求,有一些字符不能为0,比如W、G和D。排列组合问题的穷举多使用多重循环,看样子这个穷举算法应该是9重循环了,在每层循环中对一个字母进行从0到9遍历。问题是,必须这样吗,对于更通用的情况,不是9个字母的问题怎么办?首先思考一下是否每次都要遍历0-9。题目要求每个字母代表一个数字,而且不重复,很显然,对每个字母进行的并不是排列,而是某种形式的组合,举个例子,就是如果W字母占用了数字7,那么其它字母就肯定不是7,所以对D字母遍历是就可以跳过7。进一步,假设某次遍历的字母二元组向量中除M字母外其它8个字母已经有对应的数字了,比如:
[ {‘W’, 7}, {‘D’, 5}, {‘O’, 8}, {‘T’, 9}, {‘G’, 1}, {‘L’, 0}, {‘E’, 3}, {‘C’, 4}, {‘M’, ?} ] (序列-1)
那么M的可选范围就只有2和6,显然没必要使用9重循环。
现在换一种想法,对9个二元组的向量进行遍历,可以分解为两个步骤,首先确定第一个二元组的值,然后对剩下的8个二元组进行遍历。显然这是一种递归的思想(分治),算法很简单,但是要对10个数字的使用情况进行标识,对剩下的二元组进行遍历时只使用没有占用标识的数字。因此还需要一个标识数字占用情况的数字二元组定义,这个二元组可以这样定义:{number, using},0-9共有10个数字,因此需要维护一个长度为10的数字二元组向量。数字二元组向量的初始值是:
[{0, false}, {1, false},{2, false},{3, false},{4, false},{5, false},{6, false},{7, false},{8, false},{9, false}] (序列-2)
每进行一重递归就有一个数字的using标志被置为true,当字母二元组向量得到(序列-1)的结果时,对应的数字二元组向量的值应该是:
[{0, true}, {1, true},{2, false},{3, true},{4, true},{5, true},{6, false},{7, true},{8, true},{9, true}] (序列-3)
此时遍历这个数字二元组向量就可以知道M字母的可选值只能是2或6。
穷举遍历的结束条件是每层递归中遍历完所有using标志是false的数字,最外一层遍历完所有using标志是false的数字就结束了算法。
根据题目要求,开始位置的数字不能是0,也就是W、G和D这三个字母不能是0,这是一个“剪枝”条件,要利用起来,因此,对字母二元组进行扩充成字母三元组,添加一个leading标志:{char, number, leading}。下面就是这个数学模型的C语言定义:
typedef struct
{
char c;
int value;
bool leading;
}CharItem;
typedef struct
{
bool used;
int value;
}CharValue;
Run code
Cut to clipboard
根据此数学模型初始化字母三元组和数字二元组向量:
CharItem char_item[max_char_count] =
{
{ 'W', -1, true }, { 'D', -1, true }, { 'O', -1, false },
{ 'T', -1, false }, { 'G', -1, true }, { 'L', -1, false },
{ 'E', -1, false }, { 'C', -1, false }, { 'M', -1, false }
};
CharValue char_val[max_number_count] =
{
{false, 0}, {false, 1}, {false, 2}, {false, 3},
{false, 4}, {false, 5}, {false, 6}, {false, 7},
{false, 8}, {false, 9}
};
Run code
Cut to clipboard
2. 穷举算法
建立数学模型,其实就是为了让计算机理解题目并处理相关的数据,算法就是告诉计算机如何使用这些模型中的数据。本文介绍的是穷举算法,算法的核心其实前面已经提到了,就是穷举所有的字母和数字的组合,对每种组合进行合法性判断,如果是合法的组合,就输出结果。
整个算法的核心是SearchingResult()函数,其实这个函数非常简单:
void SearchingResult(CharItem ci[max_char_count],
CharValue cv[max_number_count],
int index, CharListReadyFuncPtr callback)
{
if(index == max_char_count)
{
callback(ci);
return;
}
for(int issi = 0; issi < max_number_count; ++issi)
{
if(IsValueValid(ci[index], cv[issi]))
{
cv[issi].used = true;/*set used sign*/
ci[index].value = cv[issi].value;
SearchingResult(ci, cv, index + 1, callback);
cv[issi].used = false;/*clear used sign*/
}
}
}
Run code
Cut to clipboard
SearchingResult()函数有四个参数,ci就是存储遍历结果的字母三元组向量,cv是存储遍历过程中数字占用情况的数字二元组向量,index是当前处理的字母三元组在字母三元组向量中的位置索引,0表示第一个字母三元组。callback是一个回调函数,当ci中所有三元组都分配了数字,就调用callback对这组解进行判断,如果满足算式就输出结果。SearchingResult()函数的代码分两部分,前一部分是结束条件判断和结果输出,后一部分是算法的关键。算法就是遍历cv中的所有数字二元组,对于每一个可用的数字(当前没有被占用,并且满足第一个数字不是0的要求),首先设置占用标志,然后将当前字母三元组的值与这个数字的值绑定,最后递归处理下一个字母三元组。
SearchingResult()函数是一个通用过程,负责字母和数字的组合,回调函数(callback)负责根据题目要求对SearchingResult()函数得到的字母和数字的组合进行筛选,只输出正确的组合。对于本题,回调函数可以这样实现:
void OnCharListReady(CharItem ci[max_char_count])
{
char *minuend = "WWWDOT";
char *subtrahend = "GOOGLE";
char *diff = "DOTCOM";
int m = MakeIntegerValue(ci, minuend);
int s = MakeIntegerValue(ci, subtrahend);
int d = MakeIntegerValue(ci, diff);
if((m - s) == d)
{
std::cout << m << " - " << s << " = " << d << std::endl;
}
}
Run code
Cut to clipboard
3. 结果验证
根据char_item和char_val的初始数据,求解本题的Google方程式:
SearchingResult(char_item, char_val, 0, OnCharListReady);
穷举算法可以得到两个结果(M和E可以互换):
777589 – 188103 = 589486
777589 – 188106 = 589483
由于算法具有通用性,对于前文例子中的等式:
AAB – BBC = CCD
只需要构造新的字母三元组向量,并修改回调函数的过滤数据即可。新的字母三元组可按照如下方式构造:
CharItem char_item[max_char_count] = { {'A', -1, true}, {'B', -1, true}, {'C', -1, true},
{'D', -1, false} };
Run code
Cut to clipboard
回调函数与前文的OnCharListReady()函数类似,此处不再列出。根据新的字符三元组和回调函数运行算法,可以得到13组结果:
443 – 331 = 112
553 – 332 = 221
554 – 441 = 113
665 – 551 = 114
774 – 443 = 331
775 – 552 = 223
776 – 661 = 115
885 – 553 = 332
886 – 662 = 224
887 – 771 = 116
995 – 554 = 441
997 – 772 = 225
998 – 881 = 117
对于加法、乘法和除法算式,同样只要使用不用的回调函数进行结果判断即可,不需要修改SearchingResult()函数,例如加法算式:
ABC + ABC = BCE
可以得到5组结果:
124 + 124 = 248
125 + 125 = 250
249 + 249 = 498
374 + 374 = 748
375 + 375 = 750
PHP 数组元素所有组合穷举解决方案
<?php
/*
有一个字符组成的等式:WWWDOT – GOOGLE = DOTCOM,每个字符代表一个0-9之间的数字,WWWDOT、GOOGLE和DOTCOM都是合法的数字,不能以0开头。请找出一组字符和数字的对应关系,使它们互相替换,并且替换后的数字能够满足等式。
*/
// WWWDOT
$t1 = microtime(true);
// ... 执行代码 ...
//php 解决方案 通过数组内所有元素所有排序组合穷举所有结果
$source = array('w','d','o','t','g','l','e','c','m','x');//加x元素保证有0-9个数字
//$source = array('x','w','d','o','t','g','l','e','c','m');
sort($source); //保证初始数组是有序的
$last = count($source) - 1; //$source尾部元素下标
$x = $last;
$count = 1; //组合个数统计
//echo implode(',', $source), "<br>"; //输出第一种组合
echo ss($source);
if(ss($source)){
echo implode(',', $source), "<br>"; //输出第一种组合
return true;
}
function ss($array){
//return 'asss';
$w=array_search('w', $array);
$d=array_search('d', $array);
$o=array_search('o', $array);
$t=array_search('t', $array);
$g=array_search('g', $array);
$l=array_search('l', $array);
$e=array_search('e', $array);
$c=array_search('c', $array);
$m=array_search('m', $array);
$wwwdot=$w.$w.$w.$d.$o.$t;
$google=$g.$o.$o.$g.$l.$e;
$dotcom=$d.$o.$t.$c.$o.$m;
//return $wwwdot.'='.$google.'+'.$dotcom;
$sr=$google+$dotcom;
if($sr==$wwwdot){
//echo 1111;
return $wwwdot.'='.$google.'+'.$dotcom;
}
return false;
}
while (true) {
$y = $x--; //相邻的两个元素
if ($source[$x] < $source[$y]) { //如果前一个元素的值小于后一个元素的值
$z = $last;
while ($source[$x] > $source[$z]) { //从尾部开始,找到第一个大于 $x 元素的值
$z--;
}
/* 交换 $x 和 $z 元素的值 */
list($source[$x], $source[$z]) = array($source[$z], $source[$x]);
/* 将 $y 之后的元素全部逆向排列 */
for ($i = $last; $i > $y; $i--, $y++) {
list($source[$i], $source[$y]) = array($source[$y], $source[$i]);
}
//echo implode(',', $source), "<br>"; //输出组合
$srrr=ss($source);
if($srrr){
echo $srrr.'<br />';
//return true; 不退出获取所有可能组合
}
$x = $last;
$count++;
}
if ($x == 0) { //全部组合完毕
break;
}
}
//echo 'Total: ', $count, "\n";
$t2 = microtime(true);
echo '<br />耗时'.round($t2-$t1,3).'秒';
/*这个循环好像是错的,到崩溃都不能出结果
for($w=1;$w<10;$w++){
for($d=1;$d<10;$d++){
for($o=0;$o<10;$o++){
for($t=0;$t<10;$t++){
for($g=0;$g<10;$g++){
for($l=0;$l<10;$l++){
for($e=0;$e<10;$e++){
for($c=0;$c<10;$c++){
for($m=0;$m<10;$m++){
echo $i++.'<br>';
$wwwdot=$w.$w.$w.$d.$o.$t;
$google=$g.$o.$o.$g.$l.$e;
$dotcom=$d.$o.$t.$c.$o.$m;
if($wwwdot==($google+$dotcom)){
echo '<br>'.$wwwdot.'<br>'.$google.'<br>'.$dotcom;
return true;
}
}
}
}
}
}
}
}
}
}*/
Run code
Cut to clipboard
运行结果:
原文出处: oRbIt 译文
(支付宝)给作者钱财以资鼓励 (微信)→
有过 16 条评论 »
function permute(input) { var permArr = [], usedChars = []; function main(input){ var i, ch; for (i = 0; i < input.length; i++) { ch = input.splice(i, 1)[0]; usedChars.push(ch); if (input.length == 0) { permArr.push(usedChars.slice()); } main(input); input.splice(i, 0, ch); usedChars.pop(); } return permArr } return main(input); }; console.log(permute([5, 3, 7, 1]));
var arr1=[3,9,2,7,0,8,4]; for(var i=0;i<arr1.length;i++){ for(var j=i+1;j<arr1.length;j++){ var temp=0; if(arr1[i]>arr1[j]){ temp=arr1[i]; arr1[i]=arr1[j]; arr1[j]=temp; } } } alert(arr1);
二.快速排序
var a=[3,5,0,9,2,7,5]; function quickSort(arr){ var len=a.length; if(len<=1) return arr; function sort(low,height){ var pivot=a[low]; var i=low,j=height,t; if(i>j) return false; while(i!=j){ while(a[j]>=pivot&&i<j){ j--; } while(a[i]<=pivot&&i<j){ i++; } if(i<j){//把比pivot大的换到右边,小的换到左边 t=a[i]; a[i]=a[j]; a[j]=t; } } //此时i和j指向同一个数字,把这个数字和pivot交换 a[low]=a[i]; a[i]=pivot; //递归:把pivot左边的数进行一次排序,右边的数进行一次排序 sort(low,i-1); sort(i+1,height); } //调用这个排序的函数 sort(0,len-1); return a; } alert(quickSort(a));
通过计算斐波那契数列明白缓存在网站开发中的重要性
1 、首先测试一下没有使用数据缓存来计算斐波那契数列。
var count = 0; function fib(n) { count++; if(n === 0 || n === 1) { return 1; } return fib(n - 1) + fib(n - 2); } fib(40); console.log(count);//count是函数fib运算次数,当fib(40)时候运行次数高达:331160281次,感兴趣的可以检测一下。
2、下面我们看看使用数据缓存来计算斐波那契数列。
// 缓存是怎么使用的? // 一般的缓存结构就是:键值对(对象 或者 数组) // var obj = {}; // var arr = []; // 使用缓存的方式: // 1 先去缓存中查找有没有键对应的数据 // 2 如果有,就直接拿过来使用 // 3 如果没有,就计算或者是查询到,然后,要放到缓存中! // 4 以后,就通过缓存来去数据 // 简化的步骤:先去缓存中查询有没有,如果没有就计算,并将结果放到缓存中 // 问题:缓存放到什么位置去?? // 如果是全局变量,此时,任何人都能修改全局变量的内容 // 那就造成:数据不准确 // var obj = {}; // 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89.... var count = 0; var fib = (function() { // 使用闭包,将缓存放到闭包中去, // 此时,就对这个缓存起到了保护的作用 // var cache = {}; var cache = []; // 这个函数使用来计算:斐波那契数列 return function(n) { count++; // 1 先去缓存中查找有没有该键对象的值 if(cache[n] !== undefined) { // 2 如果有,就直接返回当前的值 return cache[n]; } // 3 如果没有,就计算,然后要添加到缓存中去 if(n === 0 || n === 1) { // return 1; cache[n] = 1; return cache[n]; } var temp = arguments.callee(n - 1) + arguments.callee(n - 2); // 放到缓存中去 cache[n] = temp; return cache[n]; }; })(); fib(40); console.log(count);// 计算次数为:79次。
通过上面两次对比,瞬间觉得数据缓存的重要性,由331160281次下降到79次。感兴趣的屌丝可以测试一下fib(50);的运行次数,不使用数据缓存我测试了,我的浏览器崩溃了,使用数据缓存运行才99次。
尾递归很有意思~~~
拿快排说事的话,本来按照通俗易懂的理解,就是两次调用本身来进行左右排序。
按照尾递归的话得,可以省下一半的递归空间,直接拿一次的处理结果的low 或者high当参数传入下一次的调用。整个过程相比于同时进行两次本身的递归调用,这样只使用一次本身递归调用。
尾递归就是用迭代来代替递归全部过程。
递归调用会消耗大量的栈空间,每一次递归调用函数都会新开一个栈空间进行运行,同时要将上一次函数计算结果保存起来。所谓的保存现场,这样会消耗大量的栈空间。于是用迭代、用尾递归代替全递归调用就是一个很好的选择。
最常用距离都是斐波拉契数列,
Feb(n) = n*(n-1)*(n-2)….2*1;
教科书就是用这个进行举例递归调用的。
function Feb($n) { if($n<=2) { return $n; } $res = $n*Feb($n-1); return $res; }
这样就是一个递归调用的最经典的例子,不过在计算的过程中,每次调用的时候都得将上次计算的过程进行现场保存。所以当n很大的时候,这个n会占用很大的内存。
所以改用迭代的过程,或者尾递归的过程,即把每次的计算的结果存储起来同时传递给下一次函数得调用。
function Febs($n,$sum) { if($n<=2) { return $sum; } else { return Febs($n-1,$sum*$n); } }
在快排改进排序中也是同样利用while迭代,来替代另外一次的递归调用。
function Partion(&$arr,$low,$high) { if($low >= $high) { return; } //标记位 $povion = $arr[$low]; while ($low <$high ) { while ($low < $high && $arr[$high] <= $povion) { $high--; } $arr[$low] = $arr[$high]; while ($low < $high && $arr[$low] > $povion) { $low++; } $arr[$high] = $arr[$low]; } //循环完了之后将标记位回复到i=j相交的位置 $arr[$low] = $povion; return $low; } function Qsort(&$arr,$low,$high) { if($low >= $high ) { return; } while($low < $high) { $povision = Partion($arr,$low,$high); Qsort($arr,$low,$povision-1); $low = $povision+1; } //正常的递归调用快排算法如下 // $povision = Partion($arr,$low,$high); // Qsort($arr,$low,$povision); // Qsort($arr,$povision+1,$high); }
突然发现一个问题,我原来混淆了斐波拉契数列。把n的阶乘当成斐波拉契数列数列啦。斐波拉契数列定义应该是
Feb(n) = Feb(n-1)+Feb(n-2);
代码同理:
function Feb($n) { if($n<=2) { return 1; } $res = Feb($n-1)+Feb($n-2); return $res; } $res = Feb(20); print_r($res); function Febs($n,$sum1,$sum2) { if($n<2) { return $sum1; } else { return Febs($n-1,$sum2,$sum1+$sum2); } } print_r(Febs(20,1,1));
其实所谓尾递归,还是递归调用。但是内部就是用的所谓的迭代。其实可以用while循环来改写。将else部分替换成while的话,可能会更显而易见。
有这样一个问题,有a,b,c,d四个字母,每当刷新页面时会以随机的顺序出现,比如a,d,b,c和b,d,c,a。
我当时的解决方案为:
<script type="text/javascript"> var str1; var str=[]; var flag1=true; var flag2=true; var flag3=true; var flag4=true; do{ var num=parseInt(Math.round(Math.random()*(4-1))+1); if(num==1&&flag1){ str[str.length]="a"; flag1=false; }else if(num==2&&flag2){ str[str.length]="b"; flag2=false; }else if(num==3&&flag3){ str[str.length]="c"; flag3=false; }else if(num==4&&flag4){ str[str.length]="d"; flag4=false; } }while(flag1||flag2||flag3||flag4) str1=str[0]+str[1]+str[2]+str[3]; document.write(str1); </script>
不过这样写有着很明显的弊端,那就是下次如果想换一些字母进行输出时,必须将程序进行一次大的更改才能继续进行应用,这样做不利于重复使用
下面这种方法就很好的解决了这个问题,当想再次调用时,只需将数组arr中的值进行更改就可以了
<script type="text/javascript"> var arr=["a","b","c","d"]; function changestr(){ var newArry=[]; var temp=[];//新创建的两个数组 for(var p in arr){ temp[p]=arr[p];//将原本数组中的值复制给重新定义的数组 } for(var i=0;i<arr.length;i++){ var random=Math.round(Math.random()*(temp.length-1));//产生0~temp数组长度-1的值 newArry[i]=temp[random];//将temp数组中随机出来的数组值赋给新数组 temp.splice(random,1);//删除赋给新数组的值,temp数组的长度减一,直到temp中没有值为止 } return newArry } document.write(Arry=changestr()); </script>
在我看来,真正好的函数必须做到可重复使用,模块化,和具有良好的封装性。
找了个最精简的写法
function randomsort(a, b) { return Math.random()>.5 ? -1 : 1;//用Math.random()函数生成0~1之间的随机数与0.5比较,返回-1或1 } var arr = ["a","b","c","d"]; arr.sort(randomsort); document.write(arr);
var num = 74; for(one = 0; one <= num; one++){ for(five = 0; five <= num/5 ; five++){ for(ten = 0; ten <= num/5 ; ten++){ for(twenty = 0; twenty <= num/20; twenty++){ if(one + five*5 + ten*10 + twenty*20 == num){ console.log(one + "个1," + five + "个5," + ten +"个10," + twenty + "个20组成。"); } } } } }
伴随着Touch ID功能的发布,全世界的安全专家和骇客都蠢蠢欲动,大家都很想知道,究竟会是谁、又需要花多少时间,第一个破解这套“极度安全”的系统?于是,骇客同时用上了群众募资和游戏化两个元素,先筹建一个名为“Touch ID被破解了吗”的网站,由所有骇客捐款提供2万美元奖金,并设有排行榜显示距离2万美元募资目标还有多远。
到最后,大奖落到了混沌电脑俱乐部代号为“Starbug”的骇客手中。他巧妙地想出:在手机真正的主人使用手机,在荧幕上留下指纹之后,Starbug就将指纹照成一张2400dpi的调解析度相片,输入Photoshop,清掉杂讯,黑白反相,再打印到描图纸上。经过蚀刻得到指纹后涂上白膠,在白膠干燥后,按到Touch ID感应器,就能为手机解锁。