Clean Solution of Car Fueling problem with JavaScript - javascript

Car Fueling Problem is :
You are going to travel to another city that is located 𝑑 miles away from your home city. Your car can travel at most 𝑚 miles on a full tank and you start with a full tank. Along your way, there are gas stations at distances stop1 stop2 . . . ,stopN from your home city. What is the minimum number of refills needed?
Sample 1
Input:
950
400
4
200 375 550 750
Output:
2
The distance between the cities is 950, the car can travel at most 400
miles on a full tank. It suffices to make two refills: at points 375
and 750. This is the minimum number of refills as with a single refill
one would only be able to travel at most 800 miles.
Sample 2
Input:
10
3
4
1 2 5 9
Output:
-1
One cannot reach the gas station at point 9 as the previous gas station is too far away.
I have solved that problem with the below codes but I didn't satisfied with my solution. I think my solution can be more clear than now. Especially, input part which taking inputs from the terminal and sending those inputs to findMinFuelStops function.
May I know how can I use fewer codes to reach the same results and how can I use that approach for my other algorithms?
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
});
process.stdin.setEncoding("utf8");
let inputLines = [];
let fuelArr = [];
rl.on("line", readLine);
function readLine(line) {
let distanceDestination = 0;
let carCap = 0;
let availableStops = 0;
if (inputLines[2] == undefined) {
inputLines.push(line.toString().split(" ").map(Number));
} else {
availableStops = inputLines[2][0];
fuelArr.push(line.toString().split(" ").map(Number));
if (fuelArr[0].length > 1) {
fuelArr = fuelArr[0];
distanceDestination = inputLines[0][0];
carCap = inputLines[1][0];
console.log(
findMinFuelStops(distanceDestination, carCap, availableStops, fuelArr)
);
process.exit();
} else {
if (fuelArr.length == availableStops) {
fuelArr = fuelArr.join(" ").toString().split(" ").map(Number);
distanceDestination = inputLines[0][0];
carCap = inputLines[1][0];
console.log(
findMinFuelStops(distanceDestination, carCap, availableStops, fuelArr)
);
process.exit();
}
}
}
}
function findMinFuelStops(
distanceDestination,
carCap,
availableStops,
fuelArr
) {
let countStops = 0;
let tookDistance = 0;
const tempCarCap = carCap;
if (carCap < distanceDestination) {
for (let i = 0; i < availableStops; i++) {
if (carCap >= fuelArr[i] - tookDistance) {
carCap -= fuelArr[i] - tookDistance;
tookDistance = fuelArr[i];
} else {
carCap = tempCarCap;
if (carCap < fuelArr[i] - tookDistance) {
countStops = -1;
return countStops;
}
carCap -= fuelArr[i] - tookDistance;
tookDistance = fuelArr[i];
countStops++;
}
}
if (carCap >= distanceDestination - tookDistance) {
carCap = distanceDestination - tookDistance;
tookDistance = distanceDestination;
} else {
carCap = tempCarCap;
if (carCap < distanceDestination - tookDistance) {
countStops = -1;
return countStops;
}
tookDistance = distanceDestination;
countStops++;
carCap -= distanceDestination - tookDistance;
}
}
return countStops;
}

It seems like it should be relatively straightforward. Iterate over the possible stops. If the next stop is past the tank capacity and the current stop is not past the other city, add the current stop and reset the tank.
If there's a possibility that no path exists, take that into consideration too.
const fn = (targetDistance, tankDistance, stops) => {
let stopsNeeded = 0;
let lastStop = 0; // the mileage location of the last stop
for (let i = 0; i < stops.length; i++) {
if (lastStop + tankDistance >= targetDistance) {
return stopsNeeded;
}
const stop = stops[i];
// fail if this stop is out of reach
if (stop - lastStop > tankDistance) {
return -1;
}
// next stop may be the city
const nextStop = Math.min(stops[i + 1] ?? targetDistance, targetDistance);
// If the next stop is out of reach, and this stop is needed, stop here
if (nextStop - lastStop > tankDistance && stops[i] < targetDistance) {
stopsNeeded++;
lastStop = stop;
}
}
return targetDistance - lastStop <= tankDistance ? stopsNeeded : -1;
}
console.log(fn(950, 400, [200, 375, 550, 750]));
console.log(fn(10, 3, [1, 2, 5, 9]));

One of good approach code is on below
function minRefuelStops(target, startFuel, stations) {
let stopsNeeds = 0;
let maxR = startFuel; //The point of car maximum can reach.
let stationI = 0;
while (maxR < target && stationI < stations.length) {
while (maxR >= stations[stationI] && stationI < stations.length) {
stationI++;
}
maxR = stations[stationI - 1] + startFuel;
stopsNeeds++;
const nextStop= stations[stationI] ==undefined ? target - stations[stationI - 1] : stations[stationI] ;
if (maxR < nextStop) {
return -1;
}
}
return target <= maxR ? stopsNeeds : -1;
}
console.log(minRefuelStops(950, 400, [200, 375, 550, 750]));
console.log(minRefuelStops(10, 3, [1, 2, 5, 9]));

Related

I generate 2 arrays of equal length (15), with a choice of 4 possible letters at each position - why do they come back 53% to 87% similar?

This is inspired by a Codecadamey project, where I'm learning JavaScript.
The problem is meant to simulate a simple DNA strand. It has 15 positions in the array, and those elements are either an A, T, C, or G to represent DNA bases.
There are no limits to the amount of times a single letter (base) can show up in the array.
I create 30 arrays that are made up of at least 60% C and/or G in any of the positions, these are meant to represent strong DNA strands.
I compare the strands to each other to see what % match they are. I consider a 'match' being true when there is the same base at the same position thisDNA[i] === comparisonDNA[i]
When I test a batch of 30 of these 'strong' samples to see the best and worst match levels, I find the results very tightly grouped (I ran it 3,000 times and the highest was 87%, lowest 53%), yet it is very easy for me to concieve of two samples that will survive that are a 0% match:
const sample1 = AGACCGCGCGTGGAG
const sample2 = TCTGGCGCGCACCTC
(obviously I've cheated by building these to be a 0% match and not randomly generating them)
Here's the full code: https://gist.github.com/AidaP1/0770307979e00d4e8d3c83decc0f7771
My question is as follows: Why is the grouping of matches so tight? Why do I not see anything below a 53% match after running the test thousands of times?
Full code:
// Returns a random DNA base
const returnRandBase = () => {
const dnaBases = ['A', 'T', 'C', 'G']
return dnaBases[Math.floor(Math.random() * 4)]
}
// Returns a random single stand of DNA containing 15 bases
const mockUpStrand = () => {
const newStrand = []
for (let i = 0; i < 15; i++) {
newStrand.push(returnRandBase())
}
return newStrand
}
const pAequorFactory = (num, arr) => { //factory function for new strand specimen
return {
specimenNum: num,
dna: arr,
mutate() {
//console.log(`old dna: ${this.dna}`) //checking log
let randomBaseIndex = Math.floor(Math.random() * this.dna.length) /// chooses a location to exchange the base
let newBase = returnRandBase()
while (newBase === this.dna[randomBaseIndex]) { // Rolls a new base until newBase !== current base at that position
newBase = returnRandBase()
}
this.dna[randomBaseIndex] = newBase;
//console.log(`New dna: ${this.dna}`) //checking log
return this.dna;
},
compareDNA(pAequor) { // compare two strands and output to the console
let thisDNA = this.dna;
let compDNA = pAequor.dna;
let matchCount = 0
for (i = 0; i < this.dna.length; i++) { //cycles through each array and log position + base matches on matchCount
if (thisDNA[i] === compDNA[i]) {
matchCount += 1;
};
};
let percMatch = Math.round(matchCount / this.dna.length * 100) //multiply by 100 to make 0.25 display as 25 in console log
console.log(`specimen #${this.specimenNum} and specimen #${pAequor.specimenNum} are a ${percMatch}% DNA match.`)
return percMatch;
},
compareDNAbulk(pAequor) { //as above, but does not log to console. Used for large arrays in findMostRelated()
let thisDNA = this.dna;
let compDNA = pAequor.dna;
let matchCount = 0
for (i = 0; i < this.dna.length; i++) {
if (thisDNA[i] === compDNA[i]) {
matchCount += 1;
};
};
let percMatch = Math.round(matchCount / this.dna.length * 100) //multiply by 100 to make 0.25 display as 25 in console log
return percMatch;
},
willLikelySurvive() { // looks for >= 60% of bases as either G or C
let countCG = 0;
this.dna.forEach(base => {
if (base === 'C' || base === 'G') {
countCG += 1;
}
})
//console.log(countCG) // testing
//console.log(this.dna) // testing
return countCG / this.dna.length >= 0.6
},
complementStrand() {
return this.dna.map(base => {
switch (base) {
case 'A':
return 'T';
case 'T':
return 'A';
case 'C':
return 'G';
case 'G':
return 'C';
}
})
} //close method
} // close object
} // close factory function
function generatepAequorArray(num) { // Generatess 'num' pAequor that .willLikelySurvive() = true
let pAequorArray = []; //result array
for (i = 0; pAequorArray.length < num; i++) {
let newpAequor = pAequorFactory(i, mockUpStrand()); // runs factory until there are 30 items in the result array
if (newpAequor.willLikelySurvive() === true) {
pAequorArray.push(newpAequor)
}
}
return pAequorArray;
}
function findMostRelated(array) { // champion/challenger function to find the most related specimens
let winningScore = 0;
let winner1;
let winner2;
for (let i = 0; i < array.length; i++) {
for (let j = i; j < array.length; j++) // j = i to halve the number of loops. i = 0, j = 5 is the same as i = 5, j = 0
if (i !== j) { // Do not check specimen against itself
let currentScore = array[i].compareDNAbulk(array[j]);
if (currentScore > winningScore) { // Challenger becomes the champion if they are a closer match
winningScore = currentScore;
winner1 = array[i].specimenNum;
winner2 = array[j].specimenNum;
}
}
}
let resultArray = [winner1, winner2, winningScore] // stored together for easy return
//console.log(`The most related specimens are specimen #${winner1} and specimen #${winner2}, with a ${winningScore}% match.`)
return resultArray
}
function multiArray(loops) { //test by running finding the closest match in 30 random 'will survive' samples, repaeated 1000 times. Returns the highest and lowest match across the 1000 runs
let highScore = 0;
let lowScore = 100
for (let i = 0; i < loops; i++) {
let pAequorArray = generatepAequorArray(30);
let currentArray = findMostRelated(pAequorArray);
highScore = Math.max(highScore, currentArray[2])
lowScore = Math.min(lowScore, currentArray[2])
}
return results = {
'high score': highScore,
'low score': lowScore
}
}
console.log(multiArray(10000))

Change chances of picking a random string

I would like Anna to have 67% chance of being picked randomly, Bob to have a 30% change, and Tom to have a 3% chance. Is there a simpler way to do this?
This is what I have so far:
var nomes = ['Anna', 'Bob', 'Tom'];
var name = nomes[Math.ceil(Math.random() * (nomes.length - 1))];
console.log(name);
Based on this Stack Overflow I think the following code will work for you:
function randomChoice(p) {
let rnd = p.reduce((a, b) => a + b) * Math.random();
return p.findIndex(a => (rnd -= a) < 0);
}
function randomChoices(p, count) {
return Array.from(Array(count), randomChoice.bind(null, p));
}
const nomes = ['Anna', 'Bob', 'Tom'];
const selectedIndex = randomChoices([0.67, 0.3, 0.03], nomes);
console.log(nomes[selectedIndex]);
// Some testing to ensure that everything works as expected:
const odds = [0, 0, 0];
for (let i = 0; i < 100000; i++) {
const r = randomChoices([0.67, 0.3, 0.03], nomes);
odds[r] = odds[r] + 1;
}
console.log(odds.map(o => o / 1000));
Here's a short solution for you:
function pick(){
var rand = Math.random();
if(rand < .67) return "Anna";
if(rand < .97) return "Bob";
return "Tom";
}
console.log( pick() );

Look up tables and integer ranges - javascript

So I am looking to create look up tables. However I am running into a problem with integer ranges instead of just 1, 2, 3, etc. Here is what I have:
var ancient = 1;
var legendary = 19;
var epic = 251;
var rare = 1000;
var uncommon = 25000;
var common = 74629;
var poolTotal = ancient + legendary + epic + rare + uncommon + common;
var pool = general.rand(1, poolTotal);
var lootPool = {
1: function () {
return console.log("Ancient");
},
2-19: function () {
}
};
Of course I know 2-19 isn't going to work, but I've tried other things like [2-19] etc etc.
Okay, so more information:
When I call: lootPool[pool](); It will select a integer between 1 and poolTotal Depending on if it is 1 it will log it in the console as ancient. If it hits in the range of 2 through 19 it would be legendary. So on and so forth following my numbers.
EDIT: I am well aware I can easily do this with a switch, but I would like to try it this way.
Rather than making a huge lookup table (which is quite possible, but very inelegant), I'd suggest making a (small) object, choosing a random number, and then finding the first entry in the object whose value is greater than the random number:
// baseLootWeight: weights are proportional to each other
const baseLootWeight = {
ancient: 1,
legendary: 19,
epic: 251,
rare: 1000,
uncommon: 25000,
common: 74629,
};
let totalWeightSoFar = 0;
// lootWeight: weights are proportional to the total weight
const lootWeight = Object.entries(baseLootWeight).map(([rarity, weight]) => {
totalWeightSoFar += weight;
return { rarity, weight: totalWeightSoFar };
});
console.log(lootWeight);
const randomType = () => {
const rand = Math.floor(Math.random() * totalWeightSoFar);
return lootWeight
.find(({ rarity, weight }) => weight >= rand)
.rarity;
};
for (let i = 0; i < 10; i++) console.log(randomType());
Its not a lookup, but this might help you.
let loots = {
"Ancient": 1,
"Epic": 251,
"Legendary": 19
};
//We need loots sorted by value of lootType
function prepareSteps(loots) {
let steps = Object.entries(loots).map((val) => {return {"lootType": val[0], "lootVal": val[1]}});
steps.sort((a, b) => a.lootVal > b.lootVal);
return steps;
}
function getMyLoot(steps, val) {
let myLootRange;
for (var i = 0; i < steps.length; i++) {
if((i === 0 && val < steps[0].lootVal) || val === steps[i].lootVal) {
myLootRange = steps[i];
break;
}
else if( i + 1 < steps.length && val > steps[i].lootVal && val < steps[i + 1].lootVal) {
myLootRange = steps[i + 1];
break;
}
}
myLootRange && myLootRange['lootType'] ? console.log(myLootRange['lootType']) : console.log('Off Upper Limit!');
}
let steps = prepareSteps(loots);
let pool = 0;
getMyLoot(steps, pool);

Perceptron Javascript Inconsistencies

Building a basic Perceptron. My results after training are very inconsistent, even after 1000's of epochs. The weights seem to adjust properly, however the model fails to accurately predict. A second pairs of eyes on the structure would be greatly appreciated, struggling to find where I went wrong. The accuracy consistently tops out at 60%.
// Perceptron
class Perceptron {
constructor (x_train, y_train, learn_rate= 0.1, epochs=10) {
this.epochs = epochs
this.x_train = x_train
this.y_train = y_train
this.learn_rate = learn_rate
this.weights = new Array(x_train[0].length)
// initialize random weights
for ( let n = 0; n < x_train[0].length; n++ ) {
this.weights[n] = this.random()
}
}
// generate random float between -1 and 1 (for generating weights)
random () {
return Math.random() * 2 - 1
}
// activation function
activation (n) {
return n < 0 ? 0 : 1
}
// y-hat output given an input tensor
predict (input) {
let total = 0
this.weights.forEach((w, index) => { total += input[index] * w }) // multiply each weight by each input vector value
return this.activation(total)
}
// training perceptron on data
fit () {
for ( let e = 0; e < this.epochs; e++) { // epochs loop
for ( let i = 0; i < this.x_train.length; i++ ) { // iterate over each training sample
let prediction = this.predict(this.x_train[i]) // predict sample output
console.log('Expected: ' + this.y_train[i] + ' Model Output: ' + prediction) // log expected vs predicted
let loss = this.y_train[i] - prediction // calculate loss
for ( let w = 0; w < this.weights.length; w++ ) { // loop weights for update
this.weights[w] += loss * this.x_train[i][w] * this.learn_rate // update all weights to reduce loss
}
}
}
}
}
x = [[1, 1, 1], [0, 0, 0], [0, 0, 1], [1, 1, 0], [0, 0, 1]]
y = [1, 0, 0, 1, 0]
p = new Perceptron(x, y, epochs=5000, learn_rate=.1)
Updated:
// Perceptron
module.exports = class Perceptron {
constructor (x_train, y_train, epochs=1000, learn_rate= 0.1) {
// used to generate percent accuracy
this.accuracy = 0
this.samples = 0
this.x_train = x_train
this.y_train = y_train
this.epochs = epochs
this.learn_rate = learn_rate
this.weights = new Array(x_train[0].length)
this.bias = 0
// initialize random weights
for ( let n = 0; n < x_train[0].length; n++ ) {
this.weights[n] = this.random()
}
}
// returns percent accuracy
current_accuracy () {
return this.accuracy/this.samples
}
// generate random float between -1 and 1 (for generating weights)
random () {
return Math.random() * 2 - 1
}
// activation function
activation (n) {
return n < 0 ? 0 : 1
}
// y-hat output given an input tensor
predict (input) {
let total = this.bias
this.weights.forEach((w, index) => { total += input[index] * w }) // multiply each weight by each input vector value
return this.activation(total)
}
// training perceptron on data
fit () {
// epochs loop
for ( let e = 0; e < this.epochs; e++) {
// for each training sample
for ( let i = 0; i < this.x_train.length; i++ ) {
// get prediction
let prediction = this.predict(this.x_train[i])
console.log('Expected: ' + this.y_train[i] + ' Model Output: ' + prediction)
// update accuracy measures
this.y_train[i] === prediction ? this.accuracy += 1 : this.accuracy -= 1
this.samples++
// calculate loss
let loss = this.y_train[i] - prediction
// update all weights
for ( let w = 0; w < this.weights.length; w++ ) {
this.weights[w] += loss * this.x_train[i][w] * this.learn_rate
}
this.bias += loss * this.learn_rate
}
// accuracy post epoch
console.log(this.current_accuracy())
}
}
}
It's just a syntactic error :)
Switch the order of the last two parameters, like this:
p = new Perceptron(x, y, learn_rate=.1, epochs=5000)
And now everything should work fine.
However, a more serious problem lies in your implementation:
You forgot the bias
With a perceptron you're trying to learn a linear function, something of the form of
y = wx + b
but what you're currently computing is just
y = wx
This is fine if what you're trying to learn is just the identity function of a single input, like in your case. But it fails to work as soon as you start doing something slightly more complex like trying to learn the AND function, which can be represented like this:
y = x1 + x2 - 1.5
How to fix?
Really easy, just initialise this.bias = 0 in the constructor. Then, in predict(), you initialise let total = this.bias and, in fit(), add this.bias += loss * this.learn_rate right after the inner-most loop.

If / else in a javascript function

I'm trying to write an if/else function for a simple game but I'm new to coding.
Chapters = 0;
Books = 0;
Pens = 1;
WarehouseRoom = 50;
function ChaptersUp(number) {
if (Pens > 0) {
if ((Chapters + number) <= 9) {
Chapters = Chapters + number;
document.getElementById("Chapters").innerHTML = Chapters;
}
else {
ChaptersOver = (Chapters + number - 10);
if (Books < WarehouseRoom) {
if (ChaptersOver <= 9) {
Books = Books + 1;
Chapters = ChaptersOver;
document.getElementById("Chapters").innerHTML = Chapters;
document.getElementById("Books").innerHTML = Books;
}
else {
BooksOver = Math.floor(ChaptersOver / 10);
Books = Books + BooksOver + 1;
Chapters = (ChaptersOver - (BooksOver * 10));
document.getElementById("Chapters").innerHTML = Chapters;
document.getElementById("Books").innerHTML = Books;
}
}
}
}
}`
I want the function to run up to the point where the Warehouse is full. Currently, if I add 11 Books (110 Chapters) at a time, the function will stop operating at 55 books, but I've already went over the limit.
Question : How can I make it stop at exactly the amount equal to WarehouseRoom?
You should try a for statment.
for(int i = 1; i > warehouseFull; i++)
{
}
i is used to for the repeat and the loop will repeat till it is equal to warehouseFull. The i++ on the end is there so that once the loop is done it adds 1 to i. You could do the same backwards if you lose x amount of books. also you don't have to declare i inside the for statesmen but if you are going to use many for statement then it will make it easier so you don't have to switch letters or declare i = 0;.

Categories

Resources