Javascript 如何使用递归返回N个硬币翻转的所有组合?
请求: 使用JavaScript编写一个接受整数的函数。整数表示硬币被翻转的次数。仅使用递归策略,返回一个包含所有可能的投币组合的数组。用“H”表示头部,“T”表示尾部。组合的顺序并不重要 例如,传入“2”将返回:Javascript 如何使用递归返回N个硬币翻转的所有组合?,javascript,recursion,combinations,coin-flipping,Javascript,Recursion,Combinations,Coin Flipping,请求: 使用JavaScript编写一个接受整数的函数。整数表示硬币被翻转的次数。仅使用递归策略,返回一个包含所有可能的投币组合的数组。用“H”表示头部,“T”表示尾部。组合的顺序并不重要 例如,传入“2”将返回: [“HH”、“HT”、“TH”、“TT”] 上下文: 我对JavaScript和递归概念都比较陌生。这纯粹是为了实践和理解,因此解决方案不一定需要与下面代码的方向相匹配;任何有用的方法或其他思考方法都是有用的,只要它是纯递归的(没有循环) 尝试: 我的尝试一开始很简单,但是随着我增加
[“HH”、“HT”、“TH”、“TT”]
上下文:
我对JavaScript和递归概念都比较陌生。这纯粹是为了实践和理解,因此解决方案不一定需要与下面代码的方向相匹配;任何有用的方法或其他思考方法都是有用的,只要它是纯递归的(没有循环)
尝试:
我的尝试一开始很简单,但是随着我增加输入,“动作”逐渐变得更加复杂。我相信这适用于2、3和4的输入。但是,5或更高的输入在输出中缺少组合。非常感谢
function coinFlips(num){
const arr = [];
let str = "";
// adds base str ("H" * num)
function loadStr(n) {
if (n === 0) {
arr.push(str);
return traverseArr();
}
str += "H";
loadStr(n - 1);
}
// declares start point, end point, and index to update within each str
let start = 0;
let end = 1;
let i = 0;
function traverseArr() {
// base case
if(i === str.length) {
console.log(arr);
return arr;
}
// updates i in base str to "T"
// increments i
// resets start and end
if(end === str.length) {
str = str.split('');
str[i] = "T";
str = str.join('');
i++;
start = i;
end = i + 1;
return traverseArr();
}
// action
let tempStr = str.split('');
tempStr[start] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = str.split('');
tempStr[end] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = str.split('');
tempStr[start] = "T";
tempStr[end] = "T";
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
tempStr = tempStr.split('');
tempStr.reverse();
tempStr = tempStr.join('');
if(!arr.includes(tempStr)){
arr.push(tempStr);
};
// recursive case
start++;
end++;
return traverseArr();
}
loadStr(num);
}
coinFlips(5);
如果对此感兴趣,这里有一个解决方案,它不使用递归,而是使用类型
除n为1时外,所有可能组合的列表是通过组合每次抛硬币的所有可能结果获得的:
- 二十二→ [H,T]×[H,T]→ [HH,HT,TH,TT]
- 二十三→ [H,T]×[H,T]×[H,T]→ [HHH,HHT,HTH,HTT,THH,THT,TTH,TTT]
const concat=(…n)=>n.join(“”);
海螺('H','H');//=>'HH'
海螺('H','H','T');//=>'HHT'
海螺('H','H','T','H');//=>'hht'
//...
生成n次抛硬币结果列表的函数可以这样编写:
const-outcouts=n=>Array(n).fill(['H','T']);
结果(2);//=>[H',T'],[H',T']]
结果(3);//=>[H',T'],[H',T'],[H',T']]
// ...
我们现在可以看到一种解决方案:要获得所有可能组合的列表,我们需要在所有列表中应用concat
然而,我们不想这样做。相反,我们想让concat
使用值容器,而不是单个值
以便:
concat(['H',T'],['H',T'],['H',T']);
产生与以下相同的结果:
[concat('H','H','H')
,concat('H','H','T')
,concat('H','T','H')
,海螺('H','T','T')
,concat('T','H','H')
,concat('T','H','T')
,concat('T','T','H')
,concat('T','T','T')
]
在函数式编程中,我们说我们想要concat
。在这个例子中,我将使用Ramda的函数
const flip=n=>{
const concat=liftN(n,(…x)=>x.join(“”));
返回concat(…数组(n).fill(['H','T']);
};
控制台日志(翻转(1));
控制台日志(翻转(2));
控制台日志(翻转(3));
控制台日志(翻转(4))代码>
常数{liftN}=R下面是关于如何创建此类递归函数的详细说明。我认为所描述的步骤有助于解决大量问题。它们不是灵丹妙药,但可能非常有用。但首先,我们将努力实现以下目标:
const getFlips=(n)=>
n[r+'H',r+'T'])
确定我们的算法
要递归地解决这样的问题,我们需要回答几个问题:
我们的价值是什么?
对于简单递归,它通常是单个数值参数。在所有情况下,都必须有一种方式证明我们正在朝着某种最终状态取得进展
这是一个简单的例子,很明显,我们希望在翻转次数上再次出现;让我们称之为n
我们的递归何时结束?
我们最终需要停止复发。这里,我们可以考虑当<代码> n>代码>为0时,或者当代码> N< /代码>为1时停止。任何一种选择都可能奏效。让我们暂缓一下这个决定,看看哪一个更简单
我们如何将一个步骤的答案转换为下一个步骤的答案?
为了让递归做任何有用的事情,重要的部分是根据当前步骤计算下一步的结果
(同样,对于更复杂的递归,这里可能存在复杂性。例如,我们可能必须使用所有较低的结果来计算下一个值。例如,查找。这里我们可以忽略这一点;我们的递归很简单。)
那么,我们如何将['HH',HT',TH',TT']
转换为下一步,['HH',HHT',HTH',HTT',THH',THT',TTH',TTT']
?如果我们仔细观察下一个结果,我们可以看到,在前半部分,所有元素都以“H”开头,在第二部分,它们以“T”开头。如果忽略前半个字母,则每半个字母都是输入的副本,['HH',HT',TH',TT']
。看起来很有希望!因此,我们的递归步骤可以是复制前一个结果的两个副本,第一个副本的每个值前面都有'H'
,第二个副本前面有'T'
我们的基本案例的价值是什么?
这与我们跳过的问题有关。我们不能在不知道什么时候结束的情况下说出它的结束。但要让双方都下定决心,一个好办法是倒退
要从['HH','HHT','HTH','HTT','THH','THT','TTH','TTT']
返回到['HH','HT','TH','TT']
,我们可以取前半部分并从每个结果中删除初始的'H'。让我们再来一次。从['HH',HT',TH',TT']
中,我们取上半部分,并从每个部分中删除首字母“H”,得到['H',T']
。虽然这可能是我们的停站点,但如果我们再往前走一步,会发生什么呢?取上半部分并从剩下的一个元素中删除初始的H
,我们就剩下了JU
function getFlips(n) {
// Helper recursive function
function addFlips(n, result, current) {
if (n === 1) {
// This is the last flip, so add the result to the array
result.push(current + 'H');
result.push(current + 'T');
} else {
// Let's say current is TTH (next combos are TTHH and TTHT)
// Then for each of the 2 combos call add Flips again to get the next flips.
addFlips(n - 1, result, current + 'H');
addFlips(n - 1, result, current + 'T');
}
}
// Begin with empty results
let result = [];
// Current starts with empty string
addFlips(n, result, '');
return result;
}
const getFlips = (n) =>
n <= 0
? ['']
: <something here>