This question has been bugging me ever since I got it in a Interview and couldn't figure it out and I've not been able to find a solution anywhere, especially in Javascript:
Given 4 Digits, count how many valid Times can be displayed ib a Digital Clock (in 24hr format) using those digits. Earliest Time is 00:00 and latest is 23:59.
Write a Function:
function solution(A,B,C,D);
That, given 4 intergers A,B,C,D, returns the number of valid times that can be displayed on a digital clock
Start by writing a validating function that tells you whether 4 digits make up a legit time:
function validate(A,B,C,D){
let hours = +("" + A + B),
mins = +("" + C + D);
return hours <= 23
&& hours >= 0
&& mins >= 0
&& mins < 60
}
Then given 4 digits, before you can use the above, you need to generate random orders of those 4 digits. So take any permutation function:
const permutations = arr => {
if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;
return arr.reduce(
(acc, item, i) =>
acc.concat(
permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [
item,
...val,
])
),
[]
);
};
This will take an array of numbers like permutations([1,2,4,5]). The issue is this function will generate 24 element array even if you give it [1,1,1,1]. So you need to filter the unique ones:
const unique = arr => {
return arr.reduce((ac,d) => {
if(!ac.some(perm => d.join("") === perm.join(""))){
ac.push(d);
}
return ac;
},[])
}
Now you can write your solution combining above 3 functions:
function solution (A,B,C,D){
return unique(permutations([A,B,C,D])).reduce((ac,d) => ac += validate(...d),0)
}
For example:
solution(1,1,6,1) //2
I am absolutely sure it can written tidier and more concise, I might have forgotten something as well, so take everything with a pinch of salt
You're pretty new here. A reminder if you haven't done it, please take the tour, visit the help center and read up on asking good questions. After doing some research and searching for related topics on SO, try it yourself. If you're stuck, post a minimal, reproducible example of your attempt and note exactly where you're stuck.
I wouldn't answer without your demonstrated effort if there was not already a working answer posted.
Because there are only 24 permutations of the four digits, it might be easiest just to include them directly in the code. One solution would use a simple predicate (isValidTime) on four digits to see if they constitute a valid time, and then use a string representation of each possible permutation, convert them to four-digit strings, use the [... new Set (xs)] trick to find an array of the unique ones, collect the ones that match our validity predicate, and finally return the length of that array. It might look like this:
const isValidTime = (A, B, C, D, h = 10 * A + B, m = 10 * C + D) =>
0 <= h && h <= 23 && 0 <= m && m <= 59 // ? `${A}${B}${C}${D}` : false
const countValidTimes = (A, B, C, D, x = {A, B, C, D}) =>
[... new Set ([
"ABCD", "ABDC", "ACBD", "ACDB", "ADBC", "ADCB", "BACD", "BADC",
"BCAD", "BCDA", "BDAC", "BDCA", "CABD", "CADB", "CBAD", "CBDA",
"CDAB", "CDBA", "DABC", "DACB", "DBAC", "DBCA", "DCAB", "DCBA"
] .map (([...cs]) => cs .map (c => x [c]) .join (''))
)] .filter (s => isValidTime (... s .split ('') .map (d => Number(d)))) .length
console .log (countValidTimes (8, 6, 1, 5))
//=> 2 (18:56, 16:58)
console .log (countValidTimes (1, 2, 3, 4))
//=> 10 (12:34, 12:43, 13:24, 13:42, 14:23, 14:32, 21:34, 21:43, 23:14, 23:41)
console .log (countValidTimes (1, 4, 1, 4))
//=> 3 (14:14, 14:41, 11:44)
console .log (countValidTimes (1, 1, 1, 1))
//=> 1 (11:11)
console .log (countValidTimes (8, 6, 7, 5))
//=> 0
maybe not the best solution but i think it's ez to understand. I used backtracking to resolve this problem. Faced this question in an interview too :D.
public static void main(String[] args) {
//Driver
int result = solution(6, 2, 4, 7);
System.out.println(result);
}
public static int solution(int a, int b, int c, int d) {
int[] arr = {-1, -1, -1, -1};
Map<Integer, Integer> tracking = new HashMap<>();
tracking.put(a, 0);
tracking.put(b, 0);
tracking.put(c, 0);
tracking.put(d, 0);
int[] track = {a, b, c, d};
for (int i = 0; i < track.length; i++) {
tracking.put(track[i], tracking.get(track[i]) + 1);
}
Set<String> set = new HashSet<>();
possibleTime(a, b, c, d, arr, 0, tracking,set);
return set.size();
}
public static int[] possibleTime(int a, int b, int c, int d, int[] arr, int index, Map<Integer, Integer> tracking,Set<String> set) {
if (index == 4) {
set.add(Arrays.toString(arr));
return arr;
}
int[] pos = {a, b, c, d};
for (int i = 0; i < pos.length; i++) {
arr[index] = pos[i];
tracking.put(pos[i], tracking.get(pos[i]) - 1);
if (isValidTime(arr, tracking,set)) {
index++;
arr = possibleTime(a, b, c, d, arr, index, tracking,set);
index--;
}
tracking.put(pos[i], tracking.get(pos[i]) + 1);
arr[index] = -1;
}
return arr;
}
public static boolean isValidTime(int[] arr, Map<Integer, Integer> tracking,Set<String> set) {
//check existed
for (Integer in : tracking.keySet()) {
if (tracking.get(in) < 0) {
return false;
}
}
if(set.contains(Arrays.toString(arr)))
{
return false;
}
//validate hh
Map<Integer, Integer> hh = new HashMap<>();
hh.put(1, 9);
hh.put(2, 3);
if (arr[0] != -1) {
if (hh.containsKey(arr[0])) {
if (arr[1] != -1 && arr[1] > hh.get(arr[0])) {
return false;
}
} else {
return false;
}
}
//validate mmm
if (arr[2] != -1 && arr[2] > 5) {
return false;
}
if (arr[3] != -1 && arr[3] > 9) {
return false;
}
return true;
}
def solution(A, B, C, D):
times = 0
# check all possible hours
for hour_tens in range(2):
for hour_units in range(4):
if (hour_tens == 2 and hour_units > 3):
continue
# check all possible minutes
for min_tens in range(6):
for min_units in range(10):
if (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(A)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(B)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(C)) + (str(hour_tens) + str(hour_units) + str(min_tens) + str(min_units)).count(str(D)) == 4:
times += 1
return times
Related
I've been thinking that I found solution, but tests fail (I have no access to tests).
The task:
We want to organize a chess tournament.
One day - one game.
And as entry point we have number of days - that we can afford.
Tournament rules:
We have number of teams: if number is even - we divide number of teams by 2 and that's the number of games - so they fight 1 vs 1 in pairs, who lost is excluded from the competition.
For example : 20 teams => 10 games => 10 teams left.
And if the even number of teams left - we make the same iteration: divide by 2 into pairs.
10 teams - 5 games - 5 teams left.
And now we have Odd number!
If we have odd number from start or get it in process - number of games is counted by another rule:
everyone needs to play with everyone. So the formula is = n * (n-1) / 2.
For example 5 teams = 10 games.
Or any odd number (to sum it all - if we have Odd number we always ignore first rule with pairs).
So:
We have number of days and need to find all possible team numbers for this days, it can be more than one value, for example : 3 days, number of teams that is correct is 3 and 4;
Entry point 3, function logs 3 and 4;
If that's impossible to find teams - return -1;
1 ≤ 𝑁 ≤ 1000000
Here is my solution - It ouputs correct values, but tests fail
Can someone help to find what I did wrong?
(Sorry for code mess, I had different thoughts every time)
function solve(input) {
let quadraticEquation = (number) => {
let a = 1;
let b = -1;
let c = -number*2
if(a == 0)
return 'false';
let res = {};
let D = b * b - 4 * a * c;
let tmp = [];
if(D < 0)
return 'false';
res['discriminant'] = D;
if(D == 0)
res["quadratic roots"] = (-b + Math.sqrt(D)) / (2 * a);
else if(D > 0){
tmp.push((-b + Math.sqrt(D)) / (2 * a));
tmp.push((-b - Math.sqrt(D)) / (2 * a));
res["quadratic roots"] = tmp;
}
return tmp;
}
let start = input;
let xValues = quadraticEquation(start).map(item => Math.abs(item)).filter(item => item % 2 !== 0);
let arrayToUse = []
for(i = 0; i <= xValues[xValues.length-1]; i++) {
if( i === 1) {
continue;
}
if (!(i%2)) {
continue
}
arrayToUse.push(i);
}
const answer = [];
arrayToUse.forEach(item => {
let startValue = item;
let fightsNumber = item * (item - 1) / 2;
let loop = 0;
if (fightsNumber === start) {
answer.push([startValue, loop])
} else {
do {
loop++;
fightsNumber += startValue;
if (fightsNumber === start) {
answer.push([item, loop] )
}
startValue *= 2;
} while (fightsNumber < start)
}
})
function getTeams (answer) {
const finalResult = [];
answer.forEach(item => {
if(item[1] === 0) {
finalResult.push(item[0]);
} else {
let initialValue = item[0];
for(i=0; i < item[1]; i++) {
initialValue += item[0];
item[0] *= 2;
}
finalResult.push(initialValue);
}
})
let initialValue = 2;
let fightCounter = 0;
for (i = 0; i <= start; i++) {
fightCounter += initialValue / 2
if (fightCounter === start) {
finalResult.push(initialValue);
}
initialValue *= 2;
}
return finalResult;
}
let finalString = ''
const arrayToLog = getTeams(answer).sort((a,b) => a - b);
if(arrayToLog.length !== 0) {
arrayToLog.forEach(item => {
finalString += item + '\n';
})
} else {
finalString += -1;
}
return finalString;
}
console.log(solve(325))
console.log(solve(3))
console.log(solve(15))
console.log(solve(21))
console.log(solve(10))
console.log(solve(1))
console.log(solve(5))
console.log(solve(9))
Note: for those who didn't see it, this is a followup to the earlier question Backward recurssion.
That's a lot of code for a fairly simple problem. I don't have the inclination or the time right now to go through it carefully.
So here is how I might solve this problem, using brute force to look for all the resulting number of days for any number of players up to days + 1, and then filtering out what we want from that list:
const range = (lo, hi) =>
[... Array (hi - lo + 1)] .map ((_, i) => i + lo)
const qwe = (n) =>
n % 2 == 1
? n * (n - 1) / 2
: n / 2 + qwe (n / 2)
const solve = (days) => {
const daysByPlayers = range (1, days + 1) .map (qwe)
const res = daysByPlayers .reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
return res .length == 0 ? [-1] : res
}
const tests = [325, 3, 15, 21, 10, 1, 5, 9]
tests .forEach (n => console .log (`${n}: ${JSON.stringify(solve(n))}`))
.as-console-wrapper {max-height: 100% !important; top: 0}
This matches your output as far as I can tell. But I don't know what tests you say are failing, so perhaps there's something wrong with this as well.
There is extra complexity in here because of your desire for a -1 output in the missing cases. If an empty array would serve, then this is cleaner:
const solve = (days) =>
rng (1, days + 1) .map (qwe)
.reduce ((a, d, i) => d == days ? [...a, i + 1] : a, [])
And there are many variants. This one returns the maximum number of players that can be accommodated in some number of days up to the given value:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return days - daysByPlayers .reverse () .indexOf (max) + 1
}
And this one gives you all the possible number of players available to achieve that maximum:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d == max ? [...a, i + 1] : a, [])
}
(For instance, solve (20) would return [10, 16], which are the possible number of players to handle in exactly 15 days, the largest value found no larger than 20.)
And finally, we could list all the numbers of players we could accommodate in up to a certain number of days:
const solve = (days) => {
const daysByPlayers = rng (1, days + 1) .map (qwe)
const max = Math.max (... daysByPlayers .filter (d => d <= days))
return daysByPlayers .reduce ((a, d, i) => d <= max ? [...a, i + 1] : a, [])
}
For instance, solve (20) would return [1, 2, 3, 4, 5, 6, 8, 10, 12, 16], which are the only numbers of players that could be handled in no more than 20 days.
When I brought up the quadratic formula in the earlier question it was just to note that in certain instances, we can find an exact solution, solving for d in n * (n - 1) / 2) = d. That meant that if 8 * d + 1 is a perfect square, then we can choose n to be (1 + sqrt (8 * d + 1)) / 2 to find one solution. But that's not of any real help in the general case.
I already know how to find the GCD with no methods (below), but how can I create a function that will do it with either two or more integers?
function greatest(x,y){
return x<0 || y<0 ? null : x%y===0 ? y : greatest(y, x%y);
}
console.log(greatest(64,2)); //2
console.log(greatest(88,200)); //8
//Finding the gcd of two integers using recursion
const gcd = function(x, y) {
if (!y){ //if y is zero return back x
return x;
}
return gcd(y, x % y);
}
You can use the spread syntax to group remaining arguments in an array and take the GCD one pair at a time. The LCM can be done very similarly by a similar recursion.
const gcd = function(x, y, ...z) {
if (!y && z.length > 0) {
return gcd(x, ...z);
}
if (!y) {
return x;
}
return gcd(y, x % y, ...z);
}
console.log(gcd(6, 12, 8));
console.log(gcd(9, 15, 36));
Edit: Here's LCM, as requested in the comments. Note that you need to divide multiple GCDs, you cannot group multiple GCDs together and divide.
const lcm = function(x, y, ...z) {
if (z.length == 0) {
return x * y / gcd(x, y);
}
return lcm(x * y / gcd(x, y), ...z);
}
This version uses a standard recursive binary gcd version, and then creates a gcdAll which recurs on the list of numbers supplied, bottoming out with a call to gcd:
const gcd = (a, b) =>
b == 0 ? a : gcd (b, a % b)
const gcdAll = (n, ...ns) =>
ns.length == 0 ? n : gcd (n, gcdAll (...ns))
console .log (gcdAll (1100, 495, 165)) //=> 55
I find this conceptually simple and easy to understand. But I am also intrigued by the answer from Exalted Toast, which shuffles parameters in a really useful manner.
This question needs a mix of recurstion and the spread operator (in function parameters):
//Finding the gcd of two integers using recursion
const gcd = function(x, y) {
if (!y){ //if y is zero return back x
return x;
}
return gcd(y, x % y);
}
function gcdOfMultipleNumbers(...args) {
var newArr = args.filter(a => Number.isInteger(a) && a !== 0) // remove decimals and zeros
if(newArr.length <= 0) return 0; // if no arguments remain after filtering
if(newArr.length === 1) return newArr[0]; // if one argument remains after filtering
if(newArr.length === 2) return gcd(...newArr); // if two arguments remain after filtering
// if there are more than two arguments which remain
return gcdOfMultipleNumbers(gcd(newArr.shift(), newArr.shift()), ...newArr)
}
console.log(gcd(64,2)); //2
console.log(gcd(88,200)); //8
console.log(gcdOfMultipleNumbers(64, 24, 4, 32)); // 4
console.log(gcdOfMultipleNumbers(88, 200, 44)); //4
This question reminded me of my college life. It was asked by our Professor. So, the actual math behind the scene is: gcd(a, b, c) = gcd(a, gcd(b, c))=gcd(gcd(a, b), c)... . The function you can write is here:
// the recursive function: gcd
function gcd(num1, num2) {
// base case
if (num1 === 0) {
return num2
}
return gcd(num2 % num1, num1);
}
function multiNumGCD(...args) {
let len = args.length;
let res = args[0];
for (let i = 1; i < len; i++) {
res = gcd(args[i], res);
return res;
}
}
console.log(multiNumGCD(3, 6, 7));
Hope you find it useful !!!
Here is something that worked for me
function gcd(arr) {
let numCheck = true
arr.forEach(n => {
if(isNaN(n)) numCheck = false
})
if(!numCheck) return null;
let nums = arr.map(n => parseInt(n));
if(nums.some(n => n < 1)) return null;
let lowestNum;
nums.forEach(n => {
if(lowestNum === undefined || n < lowestNum) {
lowestNum = n;
}
})
let gcd;
for(let i = 0; i <= lowestNum; i++) {
let test = nums.filter(n => !(n%i))
if(test.length !== nums.length) {null} else {
gcd = i
}
}
return gcd;
}
And to execute it, do this:
gcd([6, 9, 12]) //3
//it also works with strings
gcd(['6', '9', '12']) //3
P.S. idk for sure but by gcd you mean greatest common divisor, right?
I'm supposed to get the size of longest incremental numbers of all given numbers. I find the size in arithemetic way.. But the question is to find it in recursive fashion.. I did it but only the last value is showing up.. I dont know what's wrong...
function findmax(num, u, max) {
let a = parseInt(num);
let last = parseInt(a % 10);
let slast = parseInt((a / 10) % 10);
if (max > 0) {
console.log(u + " " + slast + " " + last);
if (slast <= last) {
// console.log("Great");
return findmax(parseInt(a / 10), u + 1, max - 1);
} else {
// Here is the problem.. If I do return findMax(parseInt(a / 10), 1, max - 1) only last value of u is shown is shown.. Now v is always undefined, idk why
v = findmax(parseInt(a / 10), 1, max - 1);
// console.log("a");
if (u >= v) return u;
else return v;
}
}
}
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a.forEach(v => {
console.log(v + " : " + findmax(v, 1, 9));
});
Outputs for the aboeve numbers are : 3, 1, 6, 4, 4, 9, 5
This solution creates findMax by converting the number to a string of digits and then passing it to streak. streak is a recursive function that takes an array of digits, and then defaults three parameters: the previous digit, the count of the current streak and the length of the longest streak.
When we run out of digits, we return the current maximum. When our current digit is at least as large as the previous one, we recur, increasing our current streak by one, and updating our maximum if the new current streak is higher. If not, we recur, starting over with the current digit as previous, a current streak of one and the existing maximum.
const streak = ([d, ...ds], prev = -1, curr = 0, max = 0) =>
d == undefined
? max
: d >= prev
? streak (ds, d, curr + 1, Math.max (max, curr + 1))
: streak (ds, d, 1, max)
const findMax = (n) =>
streak (String (n))
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a .forEach (n => console .log (`${n}: ${findMax(n)}`))
Update
There was a comment about not wanting to use arrays for this. The array was just a convenience. It allowed us to destructure the first and remaining values of our string into useful variables. But there is nothing special about it, and our algorithm works the same with only minor tweaks, if we use a string of digits instead:
const streak = (digits, prev = -1, curr = 0, max = 0) =>
digits .length == 0
? max
: digits [0] >= prev
? streak (digits .slice (1), digits [0], curr + 1, Math.max (max, curr + 1))
: streak (digits .slice (1), digits [0], 1, max)
Update 2
The wonderful answer from #Thankyou offers a challenge to do this without anything more than the basic mathematical operators and function calls. It's not hard to write a version of this same algorithm that works this way, but since we were working with digits, it was nothing that came to mind when I first read the question. Here's a variant:
const streak = (n, prev, curr, max) =>
n < 1
? max
: n % 10 <= prev
? streak (n / 10 >> 0, n % 10, curr + 1, curr + 1 > max ? curr + 1 : max)
: streak (n / 10 >> 0, n % 10, 1, max)
const findMax = (n) =>
streak (n, -1, 0, 1)
let a = [8897727547, 9876543210, 7778124589, 7778121982, 6723198999, 8000008999, 9800236046];
a .forEach (n => console .log (`${n}: ${findMax(n)}`))
Here we break a number n into it's final digit n % 10 and the rest of the digits as a single number n / 10 >> 0. If the >> is not familiar, it's a
(truncating) shift operator for integers. n >> 0 for positive numbers is equivalent to Math .floor (n). So if n is 12345, then n % 10 is 5 and n / 10 >> 0 is 1234.
There are two chief differences from the previous version. First, I've moved the initialization of the parameters from default parameters in the internal streak to arguments supplied by the wrapper function findMax. This has little to do with the main changes here, but somehow this time just felt cleaner. Second, because it's mathematically easier to break 12345 into 1234 and 5 than to break it into 1 and 2345, we work from the last digit to the first in this version, which means that we change from a >= test to a <= one, and also that we have to start max off at 1.
The new function should feel similar to the previous two versions, but in this case it does all this based on mathematical rather than String/Array versions.
Thank you, #Thankyou for noting the possibilities here!
"I was supposed to do this without any arrays..."
Programming with restrictions is fun and forces us to think about solving problems in new ways. In this answer, we will only use primitive features of JavaScript -
Numbers, 1, 2, -Infinity, etc
Comparison operators, ==, <, >=, ||, etc
Basic arithmetic, +, /, %, >>, etc
Ternary expressions, cond ? ifTrue : otherwise
Functions, (arg1, arg2, ...) => result
String and compound data types like Array or Object will not be used.
implementation
Let's start with findMax -
const findMax = n =>
streak // <- calculate streak of
( pairwise(digits(n)) // <- pairwise digits
, tuple(1, -Infinity) // <- starting with streak of 1, max streak of -Infinity
)
As we wish for functions to exist, we implement them along the way. Let's see tuple -
const tuple = (a, b) => // <- tuple of (a,b)
k => k(a, b) // <- is a continuation of (a,b)
const car = t => // <- car of a tuple
t((a, _) => a) // <- is the first element, a
const cdr = t => // <- cdr of a tuple
t((_, b) => b) // <- is the second element, b
car(tuple(7,9)) // -> 7
cdr(tuple(7,9)) // -> 9
Now let's write digits -
const nil =
Symbol("nil") // <- empty list sentinel
const digits = n =>
n < 10 // <- when n is a single digit
? tuple(n, nil) // <- tuple of single digit and nil
: tuple(n % 10, digits(n / 10 >> 0)) // <- tuple of first digit and rest of digits
The digits will be compared pair-wise. Given 8897727547 we want to compare pairs of digits and respond to pairs where the digits are increasing -
(8,8) (8,9) (9,7) (7,7) (7,2) (2,7) (7,5) (5,4) (4,7)
= < > = > < > > <
We can implement this as pairwise -
const pairwise = t =>
zip(t, skip(t, 1))
const skip = (t, n) =>
t == nil || n == 0
? t
: cdr(t)
const zip = (t0, t1) =>
t0 == nil || t1 == nil
? nil
: tuple
( tuple(car(t0), car(t1))
, zip(cdr(t0), cdr(t1))
)
Finally we implement streak. As we already saw, it takes a pairwise list of digits and an initial result tuple of 1, -Infinity -
const streak = (t, r) =>
t == nil
? cdr(r)
: call
( n =>
streak
( cdr(t)
, tuple(n, max(cdr(r), n))
)
, caar(t) >= cadr(t)
? car(r) + 1
: 1
)
Which has simple dependencies caar, cadr, max, and call -
const caar = t =>
car(car(t))
const cadr = t =>
cdr(car(t))
const max = (a, b) =>
a > b ? a : b
const call = (f, x) =>
f(x)
Now we run findMax with your input examples -
findMax(8897727547) // -> 3
findMax(9876543210) // -> 1
findMax(7778124589) // -> 6
findMax(7778121982) // -> 4
findMax(6723198999) // -> 4
findMax(8000008999) // -> 9
findMax(9800236046) // -> 5
free data types and functions
As a result of this implementation, we ended up with some new data types and generic functions which are useful in all sorts of ways -
tuple a way to group primitive data together, and even construct lists
car, cdr, caar, and cadr give us a way to access elements within our tuples
digits constructs a sequence of tuples, representing single digits of an input number
zip, skip, and pairwise give us a generic way to manipulate sequences of tuples
max for getting the maximum of two values, and call for calling a function with an argument
demo
Expand the snippet below to verify the results in your own browser -
const nil =
Symbol("nil")
const tuple = (a, b) =>
k => k(a, b)
const car = t =>
t((a, _) => a)
const cdr = t =>
t((_, b) => b)
const caar = t =>
car(car(t))
const cadr = t =>
cdr(car(t))
const digits = n =>
n < 10
? tuple(n, nil)
: tuple(n % 10, digits(n/10 >> 0)))
const skip = (t, n) =>
t == nil || n == 0
? t
: cdr(t)
const zip = (t0, t1) =>
t0 == nil || t1 == nil
? nil
: tuple
( tuple(car(t0), car(t1))
, zip(cdr(t0), cdr(t1))
)
const pairwise = t =>
zip(t, skip(t, 1))
const max = (a, b) =>
a > b ? a : b
const call = (f, x) =>
f(x)
const streak = (t, r) =>
t == nil
? cdr(r)
: call
( n =>
streak
( cdr(t)
, tuple(n, max(cdr(r), n))
)
, caar(t) >= cadr(t)
? car(r) + 1
: 1
)
const findMax = n =>
streak
( pairwise(digits(n))
, tuple(1, -Infinity)
)
console.log(8897727547, findMax(8897727547)) // 3
console.log(9876543210, findMax(9876543210)) // 1
console.log(7778124589, findMax(7778124589)) // 6
console.log(7778121982, findMax(7778121982)) // 4
console.log(6723198999, findMax(6723198999)) // 4
console.log(8000008999, findMax(8000008999)) // 9
console.log(9800236046, findMax(9800236046)) // 5
additional reading
If you're curious where names like car, cdr, caar, and cadr come from, see Racket: Pairs and Lists.
alternatives
This of course is not the only way to solve the problem without using arrays. Scott's wonderful and straightforward answer could easily be changed to use lesser features and still arrive at the correct result. As is often the case with programming, there are countless ways to solve any specific problem, each involving techniques that make distinct trade offs. Anyhow, I hope this answer was able to teach you something fun about programming with restrictions.
I am trying to implement a function in JavaScript that returns an array of all the Fibonacci numbers up until a certain number (num). During my research I came across this answer: Calculate Fibonacci numbers up to at least n. I implemented their solution in JavaScript and Python, but found that their solution has a bug in it. The problem is that the last element is sometimes wrong. Here is the code I wrote based off of the solution I found in the answer linked above.
function findFibs(num) {
if (num < 2) {
return [1,1];
} else {
var fibs = findFibs(num - 1)
if ((fibs[fibs.length - 1]) < num ) {
fibs.push(fibs[fibs.length - 1] + fibs[fibs.length - 2])
}
return fibs;
}
}
console.log(sumFibs(20));
The expected output of this code is:[ 1, 1, 3, 5, 13 ] , but the actual output is [ 1, 1, 3, 5, 13, 21 ] . What is it that I'm missing?
functional
Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutations, variable reassignments, and other side effects. Consider a more functional approach -
Given a limit and two seeds, a and b -
if seed a is greater than the input limit, return the empty result
(inductive) a is less than or equal to the limit. recur on the sub-problem (limit, b, a + b) and append it to the singleton [a]
This encodes as a pure functional expression -
const fibs = (limit, a = 1, b = 1) =>
a > limit
? [] // #1
: [ a, ...fibs(limit, b, a + b) ] // #2
console.log(fibs(20))
// [ 1, 1, 2, 3, 5, 8, 13 ]
imperative
If I were to use imperative style for this program, I think I would reach for a generator -
function* fib (a, b)
{ yield a
yield *fib(b, a + b)
}
function* fibs (limit)
{ for (const n of fib(1, 1))
if (n < limit)
yield n
else
return
}
console.log(Array.from(fibs(20)))
// [ 1, 1, 2, 3, 5, 8, 13 ]
stack-safe
Seen above in fib, we can use recursion with imperative style but because it can overflow the stack, it would make more sense to use a loop in this situation -
function* fib (a, b)
{ let t
while (true) // stack-safe, but not recursive
{ yield a
t = a
a = b
b = t + a
}
}
function* fibs (limit)
{ for (const n of fib(1, 1))
if (n < limit)
yield n
else
return
}
console.log(Array.from(fibs(20)))
// [ 1, 1, 2, 3, 5, 8, 13 ]
BigInt
The Fibonacci sequence quickly produces large numbers that JavaScript will approximate using scientific notation. The 102nd term is 927372692193079200000 and the 103rd term is 1.5005205362068963e+21. This is due to the size constraints of JavaScript's numbers. Using the newer BigInt we can get around this easily -
function* fib (a, b)
{ let t
while (true)
{ yield a
t = a
a = b
b = t + a
}
}
function* fibs (limit)
{ for (const n of fib(1n, 1n)) // <- bigint
if (n < limit)
yield n
else
return
}
for (const n of fibs(1e70))
console.log(String(n)) // <- bigint to string
StackOverflow.com truncates output to show only the last 50 lines. Check your browser's console for the full output -
...
426547842461739379460149980002442288124894678853713953114433
690168906931029935139391829792095612517948949963798093315456
1116716749392769314599541809794537900642843628817512046429889
1806885656323799249738933639586633513160792578781310139745345
2923602405716568564338475449381171413803636207598822186175234
4730488062040367814077409088967804926964428786380132325920579
7654090467756936378415884538348976340768064993978954512095813
12384578529797304192493293627316781267732493780359086838016392
20038668997554240570909178165665757608500558774338041350112205
32423247527351544763402471792982538876233052554697128188128597
52461916524905785334311649958648296484733611329035169538240802
84885164052257330097714121751630835360966663883732297726369399
137347080577163115432025771710279131845700275212767467264610201
222232244629420445529739893461909967206666939096499764990979600
359579325206583560961765665172189099052367214309267232255589801
581811569836004006491505558634099066259034153405766997246569401
941390895042587567453271223806288165311401367715034229502159202
1523202464878591573944776782440387231570435521120801226748728603
2464593359921179141398048006246675396881836888835835456250887805
3987795824799770715342824788687062628452272409956636682999616408
6452389184720949856740872794933738025334109298792472139250504213
10440185009520720572083697583620800653786381708749108822250120621
16892574194241670428824570378554538679120491007541580961500624834
27332759203762391000908267962175339332906872716290689783750745455
44225333398004061429732838340729878012027363723832270745251370289
71558092601766452430641106302905217344934236440122960529002115744
115783425999770513860373944643635095356961600163955231274253486033
187341518601536966291015050946540312701895836604078191803255601777
303124944601307480151388995590175408058857436768033423077509087810
490466463202844446442404046536715720760753273372111614880764689587
793591407804151926593793042126891128819610710140145037958273777397
1284057871006996373036197088663606849580363983512256652839038466984
2077649278811148299629990130790497978399974693652401690797312244381
3361707149818144672666187219454104827980338677164658343636350711365
5439356428629292972296177350244602806380313370817060034433662955746
8801063578447437644962364569698707634360652047981718378070013667111
14240420007076730617258541919943310440740965418798778412503676622857
23041483585524168262220906489642018075101617466780496790573690289968
37281903592600898879479448409585328515842582885579275203077366912825
60323387178125067141700354899227346590944200352359771993651057202793
97605290770725966021179803308812675106786783237939047196728424115618
157928677948851033162880158208040021697730983590298819190379481318411
255533968719576999184059961516852696804517766828237866387107905434029
413462646668428032346940119724892718502248750418536685577487386752440
668996615388005031531000081241745415306766517246774551964595292186469
1082459262056433063877940200966638133809015267665311237542082678938909
1751455877444438095408940282208383549115781784912085789506677971125378
2833915139500871159286880483175021682924797052577397027048760650064287
4585371016945309254695820765383405232040578837489482816555438621189665
7419286156446180413982701248558426914965375890066879843604199271253952
You can set a condition before push the last item:
toPush < num ? fibs.push(toPush) : 0;
function findFibs(num) {
if (num < 2) {
return [1,1];
} else {
var fibs = findFibs(num - 1)
if ((fibs[fibs.length - 1]) < num) {
let toPush = fibs[fibs.length - 1] + fibs[fibs.length - 2];
toPush < num ? fibs.push(toPush) : 0;
}
return fibs;
}
}
console.log(findFibs(20));
The reason for the output 21 is due to the wrong condition ins if statement.
(fibs[fibs.length - 1]) < num
It means where the last entry was 13 which is less than 20.
so 13 adds up with 8 and stores as 21 in fibs array.
if you change the if statement to
if ((fibs[fibs.length - 1]+ fibs[fibs.length - 2]) < num )
Then you can expect the output you needed.
new code:
function findFibs(num) {
if (num < 2) {
return [1,1];
} else {
var fibs = findFibs(num - 1)
console.log(fibs)
if ((fibs[fibs.length - 1]+ fibs[fibs.length - 2]) < num ) {
fibs.push(fibs[fibs.length - 1] + fibs[fibs.length - 2])
}
return fibs;
}
}
console.log(findFibs(20));
Break down the code first, to understand what it is doing.
function findFibs(num) {
// checking if the given num is less than 2
if (num < 2) {
return [1, 1];
} else {
// decrement the given number by 1, which is 19, 18, 17....
// and reenter the loop with it
var fibs = findFibs(num - 1)
// grab the last and second last numbers
const a = fibs[fibs.length - 1];
const b = fibs[fibs.length - 2];
// check if the last number is less than num
// AND if the sum of a+b is less than num
// see? you should also check for (a + b) < num. this is what you missed.
if (a < num && (a + b) < num) {
// if so, add a new item to the array fibs
fibs.push(a + b)
}
return fibs;
}
}
console.log(findFibs(20));
check out the push method on W3Schools
By the way, your last line of code console.log(sumFibs(20)); should be console.log(findFibs(20));. Otherwise your code does not run properly. Happy coding!
I was doing a codewar challange, and couldn't find a solution, but I really want to know how we can solve this problem.
So we getting two integers, let's say N and D and we should return a string containing exactly N letters 'n' and exactlly D letters d with no three consecutive letters being same.
For example if we get N=5 and D=3 we should return "nndnndnd" or "nbnnbbnn" or any other correct answer
another example like if we get N=1 D=4 the only accepted answer should be "ddndd"
What I did was making a helper function like this :
function generateArray (char,q){
let arr= []
for(let i=0; i<q; i++){
arr.push(char)
}
return arr
}
and inside the main function :
function solution(N, D) {
let arrayOfchar = generateArray('n',N)
arrayOfchar.reduce((prev,current,index) => {
for(let i=0; i<D; i++) {
if(prev===current) {
arrayOfchar.splice(index, 0, "d")
}
}
})
}
But I don't know hoe should I put the "d" only after two or less consecutive "n"
Anyone clue?
Rather than creating an entire array of the same character at the very start, I think it would make more sense to create the array piece-by-piece, until N and D come out to 0.
Here's one possible implementation. The general idea is to try to push whichever character count is larger, or if that's not possible due to 3-in-a-row, push the other character, and subtract the appropriate character count by one. Repeat until both counts are 0:
function solution(n, d) {
const arr = [];
function canPush(char) {
const { length } = arr;
return (arr[length - 1] !== char || arr[length - 2] !== char);
}
function push(char) {
arr.push(char);
if (char === 'n') n--;
else if (char === 'd') d--;
}
while (n > 0 || d > 0) {
if (n > d) {
if (canPush('n')) push('n');
else if (d === 0) return console.log('Impossible');
else push('d');
} else if (d >= n) {
if (canPush('d')) push('d');
else if (n === 0) return console.log('Impossible');
else push('n');
}
}
console.log(JSON.stringify(arr));
// return arr;
}
solution(5, 3);
solution(1, 4);
solution(1, 5);
solution(5, 1);
solution(2, 5);
solution(2, 6);
solution(2, 7);
Here is another solution to this interesting problem. The idea is not to go one by one but to figure which one is the larger number and then do an array of pairs of that letter while doing a simple array of the smaller and then just concat them one with another ... so you have 5 and 3 ... nn + d + nn + d + n. 2 pairs of the bigger plus one of the smaller etc.
const fillArray = (length, letter, bigNumber) => {
var arr = []
for(var index=0; index < length; index++) {
arr.push([letter, bigNumber%2 && index+1 === length ? null : letter])
}
return arr;
}
const getString = (n, d) => {
var swtch = d > n, arr = [],
bigger = {number: swtch ? d : n, letter: swtch ? 'd' : 'n', ceil: Math.ceil((swtch ? d : n)/2)},
smaller = {number: swtch ? n : d, letter: swtch ? 'n' : 'd', ceil: Math.ceil((swtch ? n : d)/2)}
if(Math.abs((bigger.number/2) - smaller.number >= 1.5)) {
return 'Not possible with given parameters!'
}
var bigWorkArray = fillArray(bigger.ceil, bigger.letter, bigger.number)
var smallWorkArray = n === d ? fillArray(smaller.ceil, smaller.letter, smaller.number) : Array(smaller.number).fill(smaller.letter)
for(var i=0; i < bigWorkArray.length; i++) {
arr.push(...bigWorkArray[i],...smallWorkArray[i] || '')
}
return arr.join('');
}
console.log(getString(5,3))
console.log(getString(1,4))
console.log(getString(1,5))
console.log(getString(5,1))
console.log(getString(2,5))
console.log(getString(2,6))
console.log(getString(2,7))