How to fill in holes on randomly generated maze? [duplicate] - javascript

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I understand the "Depth-First" maze geerating algorithm but I need a little help implementing it with Javascript.

Maze Generation at Rosetta Code contains many implementations to generate and show a maze, using the simple Depth-first search algorithm:
Code in JavaScript:
function maze(x,y) {
var n=x*y-1;
if (n<0) {alert("illegal maze dimensions");return;}
var horiz=[]; for (var j= 0; j<x+1; j++) horiz[j]= [];
var verti=[]; for (var j= 0; j<y+1; j++) verti[j]= [];
var here= [Math.floor(Math.random()*x), Math.floor(Math.random()*y)];
var path= [here];
var unvisited= [];
for (var j= 0; j<x+2; j++) {
unvisited[j]= [];
for (var k= 0; k<y+1; k++)
unvisited[j].push(j>0 && j<x+1 && k>0 && (j != here[0]+1 || k != here[1]+1));
}
while (0<n) {
var potential= [[here[0]+1, here[1]], [here[0],here[1]+1],
[here[0]-1, here[1]], [here[0],here[1]-1]];
var neighbors= [];
for (var j= 0; j < 4; j++)
if (unvisited[potential[j][0]+1][potential[j][1]+1])
neighbors.push(potential[j]);
if (neighbors.length) {
n= n-1;
next= neighbors[Math.floor(Math.random()*neighbors.length)];
unvisited[next[0]+1][next[1]+1]= false;
if (next[0] == here[0])
horiz[next[0]][(next[1]+here[1]-1)/2]= true;
else
verti[(next[0]+here[0]-1)/2][next[1]]= true;
path.push(here= next);
} else
here= path.pop();
}
return ({x: x, y: y, horiz: horiz, verti: verti});
}
function display(m) {
var text= [];
for (var j= 0; j<m.x*2+1; j++) {
var line= [];
if (0 == j%2)
for (var k=0; k<m.y*4+1; k++)
if (0 == k%4)
line[k]= '+';
else
if (j>0 && m.verti[j/2-1][Math.floor(k/4)])
line[k]= ' ';
else
line[k]= '-';
else
for (var k=0; k<m.y*4+1; k++)
if (0 == k%4)
if (k>0 && m.horiz[(j-1)/2][k/4-1])
line[k]= ' ';
else
line[k]= '|';
else
line[k]= ' ';
if (0 == j) line[1]= line[2]= line[3]= ' ';
if (m.x*2-1 == j) line[4*m.y]= ' ';
text.push(line.join('')+'\r\n');
}
return text.join('');
}
Code in Java:
public int[][] generateMaze() {
int[][] maze = new int[height][width];
// Initialize
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
maze[i][j] = 1;
Random rand = new Random();
// r for row、c for column
// Generate random r
int r = rand.nextInt(height);
while (r % 2 == 0) {
r = rand.nextInt(height);
}
// Generate random c
int c = rand.nextInt(width);
while (c % 2 == 0) {
c = rand.nextInt(width);
}
// Starting cell
maze[r][c] = 0;
// Allocate the maze with recursive method
recursion(r, c);
return maze;
}
public void recursion(int r, int c) {
// 4 random directions
int[] randDirs = generateRandomDirections();
// Examine each direction
for (int i = 0; i < randDirs.length; i++) {
switch(randDirs[i]){
case 1: // Up
// Whether 2 cells up is out or not
if (r - 2 <= 0)
continue;
if (maze[r - 2][c] != 0) {
maze[r-2][c] = 0;
maze[r-1][c] = 0;
recursion(r - 2, c);
}
break;
case 2: // Right
// Whether 2 cells to the right is out or not
if (c + 2 >= width - 1)
continue;
if (maze[r][c + 2] != 0) {
maze[r][c + 2] = 0;
maze[r][c + 1] = 0;
recursion(r, c + 2);
}
break;
case 3: // Down
// Whether 2 cells down is out or not
if (r + 2 >= height - 1)
continue;
if (maze[r + 2][c] != 0) {
maze[r+2][c] = 0;
maze[r+1][c] = 0;
recursion(r + 2, c);
}
break;
case 4: // Left
// Whether 2 cells to the left is out or not
if (c - 2 <= 0)
continue;
if (maze[r][c - 2] != 0) {
maze[r][c - 2] = 0;
maze[r][c - 1] = 0;
recursion(r, c - 2);
}
break;
}
}
}
/**
* Generate an array with random directions 1-4
* #return Array containing 4 directions in random order
*/
public Integer[] generateRandomDirections() {
ArrayList<Integer> randoms = new ArrayList<Integer>();
for (int i = 0; i < 4; i++)
randoms.add(i + 1);
Collections.shuffle(randoms);
return randoms.toArray(new Integer[4]);
}
Source, demo and some more explanations

Related

Jarvis March implemention going into an infinite loop

I implemented Jarvis march in javascript and then submitted it, turns out in some cases it goes into an infinite loop. I tried changing almost everything and it seems like the code keeps on going into an infinite loop just for a few test cases.
This is my current code, the last for loop is to get all the points lying on the lines made by the convex hull points.
function convexHull(points, n)
{
// There must be at least 3 points
if (n < 3) return;
// Find the leftmost point
let l = 0;
let hull = [];
for (let i = 1; i < n; i++)
{
if (points[i].x < points[l].x)
l = i;
else if (points[i].x == points[l].x && points[i].y < points[l].y)
l = i;
}
// Start from leftmost point, keep moving
// counterclockwise until reach the start point
// again. This loop runs O(h) times where h is
// number of points in result or output.
let p = l, q;
do
{
// Add current point to result
hull.push(points[p]);
newhull.push(points[p]);
// Search for a point 'q' such that
// orientation(p, q, x) is counterclockwise
// for all points 'x'. The idea is to keep
// track of last visited most counterclock-
// wise point in q. If any point 'i' is more
// counterclock-wise than q, then update q.
q = (p + 1) % n;
for (let i = 0; i < n; i++)
{
// If i is more counterclockwise than
// current q, then update q
if ((orientation(points[p], points[i], points[q])
== 2))
q = i;
if(p != i && orientation(points[p], points[i], points[q]) == 0
&& onSegment(points[p], points[q], points[i])) {
q = i;
}
}
p = q;
}while (p != l); // While we don't come to first point
for(let i = 0; i < hull.length; i++) {
if(i + 1 < hull.length) {
for (let j = 0; j < points.length; j++) {
if(orientation(hull[i], points[j], hull[i+1]) == 0) {
newhull.push(points[j]);
points.splice(j, 1);
j--; }
}
}
else if(i + 1 == hull.length) {
for (let j = 0; j < points.length; j++) {
if(orientation(hull[i], points[j], hull[i+1]) == 0) {
newhull.push(points[j]);
points.splice(j, 1);
j--;
}
}
}
}
}
Any help pointing out how I could improve this or get out of an infinite loop would be much appreciated.

How to solve the problem in Javascript code to generate de Bruijn sequences?

I am trying to convert some C code to JavaScript. I chose one of the simplest rules (PCR4) and removed all irrelevant parts. The goal is to generate a particular de Bruijn sequence for a particular value of n. For example, if n = 6, the output should be
0000001111110110100100110111010101100101000101111001110001100001
I found the C source code here: http://www.combos.org/bruijn:
/*
* Copyright (c) 2016 Joe Sawada, Aaron Williams, Dennis Wong, and Daniel Gabric
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// Listings of 7 de Bruijn sequences via O(n) time and space successor rules
// Four of the rules are based on the PCR and three are based on the CCR
//
// Research by Joe Sawada, Aaron Williams, Dennis Wong, and Daniel Gabric
// Programmed by Joe Sawada, Sept. 2015 - June 2016.
// Some minor modifications for usage within the Combinatorial Object Server
// by Torsten Muetze.
//---------------------------------------------------------------------------
#include<stdio.h>
#define MAX 100
// =====================
// Test if a[1..n] = 0^n
// =====================
int Zeros(int a[], int n) {
int i;
for (i=1; i<=n; i++) if (a[i] == 1) return 0;
return 1;
}
// =============================
// Test if b[1..n] is a necklace
// =============================
int IsNecklace(int b[], int n) {
int i, p=1;
for (i=2; i<=n; i++) {
if (b[i-p] > b[i]) return 0;
if (b[i-p] < b[i]) p = i;
}
if (n % p != 0) return 0;
return 1;
}
// ===========================================
// Necklace Successor Rules
// ===========================================
int Granddaddy(int a[], int n) {
int i,j,b[MAX];
j = 2;
while (j<=n && a[j] == 1) j++;
for (i=j; i<=n; i++) b[i-j+1] = a[i];
b[n-j+2] = 0;
for (i=2; i<j; i++) b[n-j+i+1] = a[i];
if (IsNecklace(b,n)) return 1-a[1];
return a[1];
}
// -------------------------------
int Grandmama(int a[], int n) {
int i,j,k,b[MAX];
j = 1;
while (j<n && a[n-j+1] == 0) b[j++] = 0;
b[j] = 1;
k = 2;
for (i=j+1; i<=n; i++) b[i] = a[k++];
if (IsNecklace(b,n)) return 1-a[1];
return a[1];
}
// -------------------------------
int PCR3(int a[], int n) {
int i,b[MAX];
for (i=1; i<n; i++) b[i] = a[i+1];
b[n] = 1;
if (IsNecklace(b,n)) return 1-a[1];
return a[1];
}
// -------------------------------
int PCR4(int a[], int n) {
int i,b[MAX];
b[1] = 0;
for (i=2; i<=n; i++) b[i] = a[i];
if (IsNecklace(b,n)) return 1-a[1];
return a[1];
}
// ===========================================
// Co-necklace Successor Rules
// ===========================================
int CCR1(int a[], int n) {
int i,j,b[MAX],c=1;
for (i=2; i<=n; i++) if (a[i] == 0) break;
for (j=i; j<=n; j++) b[c++] = a[j];
b[c++] = 1;
for (j=2; j<i; j++) b[c++] = 1-a[j];
for (i=1; i<=n; i++) b[n+i] = 1-b[i];
if (IsNecklace(b,2*n)) return a[1];
return 1-a[1];
}
// -------------------------------
int CCR2(int a[], int n) {
int i,j,b[MAX],c=1;
i = n;
while(a[i] == 0 && i >=1) i--;
if(i == 0) i = n;
for (j=i+1; j<=n; j++) b[c++] = 0;
b[c++] = 1;
for (j=2; j<=i; j++) b[c++] = 1-a[j];
for (j=1; j<=n; j++) b[n+j] = 1-b[j];
if (IsNecklace(b,2*n)) return a[1];
return 1-a[1];
}
// -------------------------------
int CCR3(int a[], int n) {
int i,b[MAX];
for (i=1; i<n; i++) b[i] = a[i+1];
b[n] = 0;
for (i=1; i<=n; i++) b[n+i] = 1-b[i];
if (IsNecklace(b,2*n) && !Zeros(b,n)) return a[1];
return 1-a[1];
}
// =====================================================================
// Generate de Bruijn sequences by iteratively applying a successor rule
// =====================================================================
void DB(int rule, int n) {
int i, new_bit, a[MAX];
for (i=1; i<=n; i++) a[i] = 0; // First n bits
do {
printf("%d", a[1]);
switch(rule) {
case 1: new_bit = Granddaddy(a,n); break;
case 2: new_bit = Grandmama(a,n); break;
case 3: new_bit = PCR3(a,n); break;
case 4: new_bit = PCR4(a,n); break;
case 5: new_bit = CCR1(a,n); break;
case 6: new_bit = CCR2(a,n); break;
case 7: new_bit = CCR3(a,n); break;
default: break;
}
for (i=1; i<=n; i++) a[i] = a[i+1];
a[n] = new_bit;
} while (!Zeros(a,n));
printf("\n");
}
// -------------------------------
void usage() {
printf("Usage: db1 [n] [rule] (n>=1, 1<=rule<=7)\n");
}
// -------------------------------
int main(int argc, char **argv) {
int i, n, rule;
if (argc < 3) {
usage();
return 1;
}
sscanf(argv[1], "%d", &n);
sscanf(argv[2], "%d", &rule);
if ((rule < 1) || (rule > 7) || (n < 1)) {
usage();
return 1;
}
DB(rule, n);
return 0;
}
This is my code in Javascript:
function Zeros(a, n) {
var i;
for (i = 0; i < n; i++) {
if (a[i] == 1) {
return 0
}
};
return 1;
};
function IsNecklace(b, n) {
var i, p = 1;
for (i = 1; i < n; i++) {
if (b[i - p] > b[i]) {
return 0
};
if (b[i - p] < b[i]) {
p = i
};
}
if (n % p !== 0) {
return 0
};
return 1;
};
function PCR4(a, n) {
var i, b = new Array(a.length);
b[0] = 0;
for (i = 1; i < n; i++) {
b[i] = a[i]
};
if (IsNecklace(b, n)) {
return 1 - a[0]
};
return a[0];
};
function DB(n) {
var i, new_bit, a = [];
for (i = 0; i < n; i++) {
a[i] = 0
};
do {
new_bit = PCR4(a, n);
for (i = 0; i < n-1; i++) {
a[i] = a[i + 1]
};
a[n - 1] = new_bit;
} while (!Zeros(a, n));
return a
}
console.log(DB(6));
But it does not work: the output is nothing but an array of zeros (instead of an array of zeros and ones corresponding to the correct output for a given n) -- no matter whether I use DB(6) or DB(64).
Where is my mistake?
The main issue is that in DB you return a. If you look at the condition of the loop just above that return, you'll see that this loop exits when a consists only of zeroes. So it is no wonder you only get zeroes in the output.
In the C-code you referred to, DB does not return anything. It prints. So if you want to make this a function that returns the result, you should collect the output in a variable at the same spot as where the C-code prints. This could be a JavaScript string, and then the function should return that string:
function DB(n) {
var i, new_bit, a = [], output = ""; // Variable for collecting output
for (i = 0; i < n; i++) {
a[i] = 0
};
do {
output += a[0]; // <--- collect
new_bit = PCR4(a, n);
for (i = 0; i < n-1; i++) {
a[i] = a[i + 1]
};
a[n - 1] = new_bit;
} while (!Zeros(a, n));
return output; // <--- return collected characters
}
A second problem occurs in IsNecklace, where you missed an aspect while converting the C-code to be 0-index-based:
This:
p = i
Should be:
p = i + 1
...because i is now a zero-based index, while p is a difference between indexes. p should get the same values, independent on which indexing you use.
Some JavaScript shortcuts
Although an almost literal translation of the code works, you might as well use some of the more powerful functions that JavaScript offers, like Array#fill, Array#includes, Array#shift, Array#push, Array.from, ...
Here is what that could look like:
function isNecklace(b) {
let p = 1;
for (let i = 1; i < b.length; i++) {
if (b[i - p] > b[i]) {
return 0;
}
if (b[i - p] < b[i]) {
p = i + 1;
}
}
return +!(b.length % p);
};
function pcr4(a) {
let b = Array.from(a);
b[0] = 0;
return a[0] ^ isNecklace(b);
};
function DB(n) {
let output = "";
let a = Array(n).fill(0);
do {
output += a[0];
let new_bit = pcr4(a);
a.shift();
a.push(new_bit);
} while (a.includes(1));
return output;
}
console.log(DB(6));

Simple Javascript Christmas Tree

I created a half of the Christmas Tree but here I got blocked. Some one please help me to understand how to do the left side too.
for (var i = 0; i < 8; i++) {
for (var j = 0; j <= i; j++) {
document.write("^");
}
document.write("<br>");
}
<pre>
<script>
//Reads number of rows to be printed
var n = 8;
for(i=1; i<=n; i++)
{
//Prints trailing spaces
for(j=i; j<n; j++)
{
document.write(" ");
}
//Prints the pyramid pattern
for(j=1; j<=(2*i-1); j++)
{
document.write("*");
}
document.write("<br>");
}
</script>
</pre>
Source: http://codeforwin.org/2015/07/equilateral-triangle-star-pattern-program-in-c.html
C to JavaScript by me.
I wrote the following code for this problem.
I also added a nice extra, christmas-tree ornaments :-)
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
private static Random RND = new Random(System.currentTimeMillis()); // useful for placing balls
private static char[] BALLS = {'o','⌾','⛣','⏣','◍'}; // symbols being used as balls
public static void main (String[] args) throws java.lang.Exception
{
int w = 27; // width of the tree
int b = 10; // number of balls in the tree
String tree = ""; // this will end up containing the tree
// build tree
w = ( w % 2 == 1 ) ? w : 13; // check whether width is odd
for(int i=1;i<=w;i+=2){
int s = (w - i) / 2;
tree += repeat(' ', s) + repeat('*', i) + repeat(' ', s) + "\n";
}
// randomly replace some parts by balls
int i=0;
while(i < b){
int j = RND.nextInt(tree.length());
if(tree.charAt(j) == '*'){
tree = tree.substring(0, j) + BALLS[RND.nextInt(BALLS.length)] + tree.substring(j+1);
i++;
}
}
// build trunk
tree += repeat(' ', (w - 4) / 2) + repeat('*', 4) + "\n" + repeat(' ', (w - 4) / 2) + repeat('*', 4);
// output
System.out.println(tree);
}
// this function builds a String by repeating a given character a couple of times
private static String repeat(char c, int l){
String s = "";
for(int i=0;i<l;i++)
s += c;
return s;
}
}
The output should look something like this:
⏣
***
*o***
**⌾*o**
*****⛣**⛣
*****⌾****⏣
**◍*◍********
****
****
The keyword is think.
var x = 8;
for (let i = 0; i < x; i++) {
for (let j=x-1; j>i; j--) {
document.write("&nbsp&nbsp");
}
for (let k=0; k<=(i*2); k++) {
document.write("^");
}
document.write("<br>");
}
for (let i=0; i<2; i++) {
for (let j=0; j<(x*2)-3; j++) {
document.write("&nbsp");
}
document.write("^<br>");
}
Constraints: Only looks good starting from x = 5.
Original code by me
The answers above heavily rely on nested loops, thought I post another approach with "modern" JS (of course still using a single loop with the map function given to Array.from()):
function xmas(height) {
// add 1 more level for the trunk, e.g. height+1
return Array.from({length: height+1}, (v, i) => {
return i === height
// that's for the trunk of the tree
? '*'.padStart(Math.round((2 * i)/2), ' ')
// the actual tree "levels"
: '*'.repeat(2 * i + 1).padStart(2 * i + height-i, ' ');
}).join('\n');
}
document.write(`<pre>${xmas(10)}</pre>`);
maybe the attempt to make it work with .padStart() is not optimal because the math gets a bit ugly, but anyways, just for fun =)...
Here's a solution with a simple for loop without any nested loop.
let row = ""
let l = 9
for (let i = 0; i < l; i++) {
row += " ".repeat(l - i) + "*" + "*".repeat(i * 2) + `\n`;
}
console.log(row);
Simple christmas tree function:
function christmasTree(x) {
if(x < 3) {
return "";
}
let tree = "";
for(let i = 1; i <= x; i++) {
for(let j = 1; j <= x + x - 1; j++) {
if(j <= x - i || j >= x + i) {
tree += " ";
} else {
tree += "*";
}
}
tree += "\n";
}
return tree;
}
Incase you are looking for how to do this in a function for javascript or typescript
Use 3 for loops,
1 - Number of rows
2 - Number of spaces
3 - Number of characters
function christmas(n) {
let tree = '';
for (let i = 1; i <= n; i++) {
for (let j=0; j <= n-i; j++) {
tree += ' ';
}
for (k = 0; k< (i*2)-1; k++) {
tree += '*';
}
tree += '\n';
}
return tree;
}
console.log(christmas(3));
<pre>
<script>
//Reads number of rows to be printed
var n = 8;
for(i=1; i<=n; i++)
{
//Prints trailing spaces
for(j=i; j<n; j++)
{
document.write(" ");
}
//Prints the pyramid pattern
for(j=1; j<=(2*i-1); j++)
{
document.write("*");
}
document.write("<br>");
}
</script>
</pre>

Compare strings with 'similar' letters [duplicate]

So I have a random javascript array of names...
[#larry,#nicholas,#notch] etc.
They all start with the # symbol. I'd like to sort them by the Levenshtein Distance so that the the ones at the top of the list are closest to the search term. At the moment, I have some javascript that uses jQuery's .grep() on it using javascript .match() method around the entered search term on key press:
(code edited since first publish)
limitArr = $.grep(imTheCallback, function(n){
return n.match(searchy.toLowerCase())
});
modArr = limitArr.sort(levenshtein(searchy.toLowerCase(), 50))
if (modArr[0].substr(0, 1) == '#') {
if (atRes.childred('div').length < 6) {
modArr.forEach(function(i){
atRes.append('<div class="oneResult">' + i + '</div>');
});
}
} else if (modArr[0].substr(0, 1) == '#') {
if (tagRes.children('div').length < 6) {
modArr.forEach(function(i){
tagRes.append('<div class="oneResult">' + i + '</div>');
});
}
}
$('.oneResult:first-child').addClass('active');
$('.oneResult').click(function(){
window.location.href = 'http://hashtag.ly/' + $(this).html();
});
It also has some if statements detecting if the array contains hashtags (#) or mentions (#). Ignore that. The imTheCallback is the array of names, either hashtags or mentions, then modArr is the array sorted. Then the .atResults and .tagResults elements are the elements that it appends each time in the array to, this forms a list of names based on the entered search terms.
I also have the Levenshtein Distance algorithm:
var levenshtein = function(min, split) {
// Levenshtein Algorithm Revisited - WebReflection
try {
split = !("0")[0]
} catch(i) {
split = true
};
return function(a, b) {
if (a == b)
return 0;
if (!a.length || !b.length)
return b.length || a.length;
if (split) {
a = a.split("");
b = b.split("")
};
var len1 = a.length + 1,
len2 = b.length + 1,
I = 0,
i = 0,
d = [[0]],
c, j, J;
while (++i < len2)
d[0][i] = i;
i = 0;
while (++i < len1) {
J = j = 0;
c = a[I];
d[i] = [i];
while(++j < len2) {
d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
++J;
};
++I;
};
return d[len1 - 1][len2 - 1];
}
}(Math.min, false);
How can I work with algorithm (or a similar one) into my current code to sort it without bad performance?
UPDATE:
So I'm now using James Westgate's Lev Dist function. Works WAYYYY fast. So performance is solved, the issue now is using it with source...
modArr = limitArr.sort(function(a, b){
levDist(a, searchy)
levDist(b, searchy)
});
My problem now is general understanding on using the .sort() method. Help is appreciated, thanks.
Thanks!
I wrote an inline spell checker a few years ago and implemented a Levenshtein algorithm - since it was inline and for IE8 I did quite a lot of performance optimisation.
var levDist = function(s, t) {
var d = []; //2d matrix
// Step 1
var n = s.length;
var m = t.length;
if (n == 0) return m;
if (m == 0) return n;
//Create an array of arrays in javascript (a descending loop is quicker)
for (var i = n; i >= 0; i--) d[i] = [];
// Step 2
for (var i = n; i >= 0; i--) d[i][0] = i;
for (var j = m; j >= 0; j--) d[0][j] = j;
// Step 3
for (var i = 1; i <= n; i++) {
var s_i = s.charAt(i - 1);
// Step 4
for (var j = 1; j <= m; j++) {
//Check the jagged ld total so far
if (i == j && d[i][j] > 4) return n;
var t_j = t.charAt(j - 1);
var cost = (s_i == t_j) ? 0 : 1; // Step 5
//Calculate the minimum
var mi = d[i - 1][j] + 1;
var b = d[i][j - 1] + 1;
var c = d[i - 1][j - 1] + cost;
if (b < mi) mi = b;
if (c < mi) mi = c;
d[i][j] = mi; // Step 6
//Damerau transposition
if (i > 1 && j > 1 && s_i == t.charAt(j - 2) && s.charAt(i - 2) == t_j) {
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
}
}
}
// Step 7
return d[n][m];
}
I came to this solution:
var levenshtein = (function() {
var row2 = [];
return function(s1, s2) {
if (s1 === s2) {
return 0;
} else {
var s1_len = s1.length, s2_len = s2.length;
if (s1_len && s2_len) {
var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
while (i1 < s1_len)
row[i1] = ++i1;
while (i2 < s2_len) {
c2 = s2.charCodeAt(i2);
a = i2;
++i2;
b = i2;
for (i1 = 0; i1 < s1_len; ++i1) {
c = a + (s1.charCodeAt(i1) === c2 ? 0 : 1);
a = row[i1];
b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
row[i1] = b;
}
}
return b;
} else {
return s1_len + s2_len;
}
}
};
})();
See also http://jsperf.com/levenshtein-distance/12
Most speed was gained by eliminating some array usages.
Updated: http://jsperf.com/levenshtein-distance/5
The new Revision annihilates all other benchmarks. I was specifically chasing Chromium/Firefox performance as I don't have an IE8/9/10 test environment, but the optimisations made should apply in general to most browsers.
Levenshtein Distance
The matrix to perform Levenshtein Distance can be reused again and again. This was an obvious target for optimisation (but be careful, this now imposes a limit on string length (unless you were to resize the matrix dynamically)).
The only option for optimisation not pursued in jsPerf Revision 5 is memoisation. Depending on your use of Levenshtein Distance, this could help drastically but was omitted due to its implementation specific nature.
// Cache the matrix. Note this implementation is limited to
// strings of 64 char or less. This could be altered to update
// dynamically, or a larger value could be used.
var matrix = [];
for (var i = 0; i < 64; i++) {
matrix[i] = [i];
matrix[i].length = 64;
}
for (var i = 0; i < 64; i++) {
matrix[0][i] = i;
}
// Functional implementation of Levenshtein Distance.
String.levenshteinDistance = function(__this, that, limit) {
var thisLength = __this.length, thatLength = that.length;
if (Math.abs(thisLength - thatLength) > (limit || 32)) return limit || 32;
if (thisLength === 0) return thatLength;
if (thatLength === 0) return thisLength;
// Calculate matrix.
var this_i, that_j, cost, min, t;
for (i = 1; i <= thisLength; ++i) {
this_i = __this[i-1];
for (j = 1; j <= thatLength; ++j) {
// Check the jagged ld total so far
if (i === j && matrix[i][j] > 4) return thisLength;
that_j = that[j-1];
cost = (this_i === that_j) ? 0 : 1; // Chars already match, no ++op to count.
// Calculate the minimum (much faster than Math.min(...)).
min = matrix[i - 1][j ] + 1; // Deletion.
if ((t = matrix[i ][j - 1] + 1 ) < min) min = t; // Insertion.
if ((t = matrix[i - 1][j - 1] + cost) < min) min = t; // Substitution.
matrix[i][j] = min; // Update matrix.
}
}
return matrix[thisLength][thatLength];
};
Damerau-Levenshtein Distance
jsperf.com/damerau-levenshtein-distance
Damerau-Levenshtein Distance is a small modification to Levenshtein Distance to include transpositions. There is very little to optimise.
// Damerau transposition.
if (i > 1 && j > 1 && this_i === that[j-2] && this[i-2] === that_j
&& (t = matrix[i-2][j-2]+cost) < matrix[i][j]) matrix[i][j] = t;
Sorting Algorithm
The second part of this answer is to choose an appropriate sort function. I will upload optimised sort functions to http://jsperf.com/sort soon.
I implemented a very performant implementation of levenshtein distance calculation if you still need this.
function levenshtein(s, t) {
if (s === t) {
return 0;
}
var n = s.length, m = t.length;
if (n === 0 || m === 0) {
return n + m;
}
var x = 0, y, a, b, c, d, g, h, k;
var p = new Array(n);
for (y = 0; y < n;) {
p[y] = ++y;
}
for (; (x + 3) < m; x += 4) {
var e1 = t.charCodeAt(x);
var e2 = t.charCodeAt(x + 1);
var e3 = t.charCodeAt(x + 2);
var e4 = t.charCodeAt(x + 3);
c = x;
b = x + 1;
d = x + 2;
g = x + 3;
h = x + 4;
for (y = 0; y < n; y++) {
k = s.charCodeAt(y);
a = p[y];
if (a < c || b < c) {
c = (a > b ? b + 1 : a + 1);
}
else {
if (e1 !== k) {
c++;
}
}
if (c < b || d < b) {
b = (c > d ? d + 1 : c + 1);
}
else {
if (e2 !== k) {
b++;
}
}
if (b < d || g < d) {
d = (b > g ? g + 1 : b + 1);
}
else {
if (e3 !== k) {
d++;
}
}
if (d < g || h < g) {
g = (d > h ? h + 1 : d + 1);
}
else {
if (e4 !== k) {
g++;
}
}
p[y] = h = g;
g = d;
d = b;
b = c;
c = a;
}
}
for (; x < m;) {
var e = t.charCodeAt(x);
c = x;
d = ++x;
for (y = 0; y < n; y++) {
a = p[y];
if (a < c || d < c) {
d = (a > d ? d + 1 : a + 1);
}
else {
if (e !== s.charCodeAt(y)) {
d = c + 1;
}
else {
d = c;
}
}
p[y] = d;
c = a;
}
h = d;
}
return h;
}
It was my answer to a similar SO question
Fastest general purpose Levenshtein Javascript implementation
Update
A improved version of the above is now on github/npm see
https://github.com/gustf/js-levenshtein
The obvious way of doing this is to map each string to a (distance, string) pair, then sort this list, then drop the distances again. This way you ensure the levenstein distance only has to be computed once. Maybe merge duplicates first, too.
I would definitely suggest using a better Levenshtein method like the one in #James Westgate's answer.
That said, DOM manipulations are often a great expense. You can certainly improve your jQuery usage.
Your loops are rather small in the example above, but concatenating the generated html for each oneResult into a single string and doing one append at the end of the loop will be much more efficient.
Your selectors are slow. $('.oneResult') will search all elements in the DOM and test their className in older IE browsers. You may want to consider something like atRes.find('.oneResult') to scope the search.
In the case of adding the click handlers, we may want to do one better avoid setting handlers on every keyup. You could leverage event delegation by setting a single handler on atRest for all results in the same block you are setting the keyup handler:
atRest.on('click', '.oneResult', function(){
window.location.href = 'http://hashtag.ly/' + $(this).html();
});
See http://api.jquery.com/on/ for more info.
I just wrote an new revision: http://jsperf.com/levenshtein-algorithms/16
function levenshtein(a, b) {
if (a === b) return 0;
var aLen = a.length;
var bLen = b.length;
if (0 === aLen) return bLen;
if (0 === bLen) return aLen;
var len = aLen + 1;
var v0 = new Array(len);
var v1 = new Array(len);
var i = 0;
var j = 0;
var c2, min, tmp;
while (i < len) v0[i] = i++;
while (j < bLen) {
c2 = b.charAt(j++);
v1[0] = j;
i = 0;
while (i < aLen) {
min = v0[i] - (a.charAt(i) === c2 ? 1 : 0);
if (v1[i] < min) min = v1[i];
if (v0[++i] < min) min = v0[i];
v1[i] = min + 1;
}
tmp = v0;
v0 = v1;
v1 = tmp;
}
return v0[aLen];
}
This revision is faster than the other ones. Works even on IE =)

Create range of letters and numbers

I'm creating a form where users can input a range. They are allowed to input letters and numbers. Some sample input:
From: AA01
To: AZ02
Which should result in:
AA01
AA02
AB01
AB02
And so on, till AZ02
And:
From: BC01
To: DE01
Should result in:
BC01
BD01
BE01
CC01
CD01
CE01
Etc
I managed to get it working for the input A01 to D10 (for example)
jsFiddle
However, i can't get it to work with multiple letters.
JS code:
var $from = $('input[name="from"]');
var $to = $('input[name="to"]');
var $quantity = $('input[name="quantity"]');
var $rangeList = $('.rangeList');
var $leadingzeros = $('input[name="leadingzeros"]');
$from.on('keyup blur', function () {
$(this).val($(this).val().replace(/[^a-zA-Z0-9]/g, ''));
updateQuantity();
});
$to.on('keyup blur', function () {
$(this).val($(this).val().replace(/[^a-zA-Z0-9]/g, ''));
updateQuantity();
});
$leadingzeros.on('click', function () {
updateQuantity();
});
function updateQuantity() {
var x = parseInt($from.val().match(/\d+/));
var y = parseInt($to.val().match(/\d+/));
var xl = $from.val().match(/[a-zA-Z]+/);
var yl = $to.val().match(/[a-zA-Z]+/);
var result = new Array();
if (xl != null && yl != null && xl[0].length > 0 && yl[0].length > 0) {
xl = xl[0].toUpperCase();
yl = yl[0].toUpperCase();
$rangeList.html('');
var a = yl.charCodeAt(0) - xl.charCodeAt(0);
for (var i = 0; i <= a; i++) {
if (!isNaN(x) && !isNaN(y)) {
if (x <= y) {
var z = (y - x) + 1;
$quantity.val(z * (a + 1));
$rangeList.html('');
for (var b = z; b > 0; b--) {
var c = ((y - b) + 1);
if ($leadingzeros.prop('checked')) {
c = leadingZeroes(c, y.toString().length);
}
result.push(String.fromCharCode(65 + i) + c);
}
} else {
$rangeList.html('');
$quantity.val(0);
}
} else {
$rangeList.html('');
$quantity.val(0);
}
}
} else if (!isNaN(x) && !isNaN(y)) {
if (x < y) {
var z = (y - x) + 1;
$quantity.val(z);
$rangeList.html('');
for (var i = z; i > 0; i--) {
var c = (y - i) + 1;
if ($leadingzeros.prop('checked')) {
c = leadingZeroes(c, y.toString().length);
}
result.push(c);
}
} else {
$rangeList.html('');
$quantity.val(0);
}
} else {
$rangeList.html('');
$quantity.val(0);
}
$rangeList.html('');
for (var i = 0; i < result.length; i++) {
$rangeList.append(result[i] + '<br />');
}
}
function leadingZeroes(number, size) {
number = number.toString();
while (number.length < size) number = "0" + number;
return number;
}
This is perfect for a recursive algorithm:
function createRange(from, to) {
if (from.length === 0) {
return [ "" ];
}
var result = [];
var innerRange = createRange(from.substring(1), to.substring(1));
for (var i = from.charCodeAt(0); i <= to.charCodeAt(0); i++) {
for (var j = 0; j < innerRange.length; j++) {
result.push(String.fromCharCode(i) + innerRange[j]);
}
}
return result;
}
Called as follows:
createRange('BC01', 'DE02'); // Generates an array containing all values expected
EDIT: Amended function below to match new test case (much more messy, however, involving lots of type coercion between strings and integers).
function prefixZeroes(value, digits) {
var result = '';
value = value.toString();
for (var i = 0; i < digits - value.length; i++) {
result += '0';
}
return result + value;
}
function createRange(from, to) {
if (from.length === 0) {
return [ "" ];
}
var result = [];
if (from.charCodeAt(0) < 65) {
fromInt = parseInt(from);
toInt = parseInt(to);
length = toInt.toString().length;
var innerRange = createRange(from.substring(length), to.substring(length));
for (var i = fromInt; i <= toInt; i++) {
for (var j = 0; j < innerRange.length; j++) {
result.push(prefixZeroes(i, length) + innerRange[j]);
}
}
} else {
var innerRange = createRange(from.substring(1), to.substring(1));
for (var i = from.charCodeAt(0); i <= to.charCodeAt(0); i++) {
for (var j = 0; j < innerRange.length; j++) {
result.push(String.fromCharCode(i) + innerRange[j]);
}
}
}
return result;
}
Please note that because of your strict logic in how the value increments this method requires exactly 4 characters (2 letters followed by 2 numbers) to work. Also, this might not be as efficient/tidy as it can be but it took some tinkering to meet your logic requirements.
function generate(start, end) {
var results = [];
//break out the start/end letters/numbers so that we can increment them seperately
var startLetters = start[0] + start[1];
var endLetters = end[0] + end[1];
var startNumber = Number(start[2] + start[3]);
var endNumber = Number(end[2] + end[3]);
//store the start letter/number so we no which value to reset the counter to when a maximum boundry in reached
var resetLetter = startLetters[1];
var resetNumber = startNumber;
//add first result as we will always have at least one
results.push(startLetters + (startNumber < 10 ? "0" + startNumber : "" + startNumber));
//maximum while loops for saefty, increase if needed
var whileSafety = 10000;
while (true) {
//safety check to ensure while loop doesn't go infinite
whileSafety--;
if (whileSafety == 0) break;
//check if we have reached the maximum value, if so stop the loop (break)
if (startNumber == endNumber && startLetters == endLetters) break;
//check if we have reached the maximum number. If so, and the letters limit is not reached
//then reset the number and increment the letters by 1
if (startNumber == endNumber && startLetters != endLetters) {
//reset the number counter
startNumber = resetNumber;
//if the second letter is at the limit then reset it and increment the first letter,
//otherwise increment the second letter and continue
if (startLetters[1] == endLetters[1]) {
startLetters = '' + String.fromCharCode(startLetters.charCodeAt(0) + 1) + resetLetter;
} else {
startLetters = startLetters[0] + String.fromCharCode(startLetters.charCodeAt(1) + 1);
}
} else {
//number limit not reached so just increment the number counter
startNumber++;
}
//add the next sequential value to the array
results.push(startLetters + (startNumber < 10 ? "0" + startNumber : "" + startNumber));
}
return results;
}
var results = generate("BC01", "DE01");
console.log(results);
Here is a working example, which uses your second test case
Using #Phylogenesis' code, i managed to achieve my goal.
jsFiddle demo
function updateQuantity() {
var x = parseInt($from.val().match(/\d+/));
var y = parseInt($to.val().match(/\d+/));
var xl = $from.val().match(/[a-zA-Z]+/);
var yl = $to.val().match(/[a-zA-Z]+/);
var result = new Array();
var r = createRange(xl[0], yl[0]);
var z = (y - x) + 1;
if (x <= y) {
for (var j = 0; j < r.length; j++) {
var letters = r[j];
for (var i = z; i > 0; i--) {
var c = (y - i) + 1;
if ($leadingzeros.prop('checked')) {
c = leadingZeroes(c, y.toString().length);
}
if (i == z) {
r[j] = letters + c + '<br />';
} else {
j++;
r.splice(j, 0, letters + c + '<br />');
}
}
}
} else {
for (var i = 0; i < r.length; i++) {
r[i] += '<br />';
}
}
$quantity.val(r.length);
$rangeList.html('');
for (var i = 0; i < r.length; i++) {
$rangeList.append(r[i]);
}
}
This works for unlimited letters and numbers, as long as the letters are first.
Thanks for your help!

Categories

Resources