During development of a pipeline in Halide,
I want to avoid unnecessary checks on buffer layouts.
I know I can turn off the majority of assertions using the 'no_asserts' target feature.
However, I generated the following simple code:
#define LUT_SIZE 17 /* Size in each dimension of the 4D LUT */
class ApplyLut : public Halide::Generator<ApplyLut> {
public:
// We declare the Inputs to the Halide pipeline as public
// member variables. They'll appear in the signature of our generated
// function in the same order as we declare them.
Input < Buffer<uint8_t>> Lut { "Lut" , 1}; // LUT to apply
Input < Buffer<int>> indexToLut { "indexToLut" , 1}; // Precalculated mapping of uint8_t to LUT index
Input < Buffer<uint8_t >> inputImageLine { "inputImageLine" , 1}; // Input line
Output< Buffer<uint8_t >> outputImageLine { "outputImageLine", 1}; // Output line
void generate();
};
HALIDE_REGISTER_GENERATOR(ApplyLut, outputImageLine)
void ApplyLut::generate()
{
Var x("x");
outputImageLine(x) = Lut(clamp(indexToLut(inputImageLine(x)), 0, LUT_SIZE));
inputImageLine .dim(0).set_min(0); // Input image sample index
inputImageLine .dim(0).set_stride(1); // Input image sample index
outputImageLine.dim(0).set_bounds(0, inputImageLine.dim(0).extent()); // Output line matches input line
outputImageLine.dim(0).set_stride( inputImageLine.dim(0).stride()); // Output line matches input line
Lut .dim(0).set_bounds(0, LUT_SIZE); //iccLut[...]: , limited number of values
Lut .dim(0).set_stride(1); //iccLut[...]: , limited number of values
indexToLut .dim(0).set_bounds(0, 256); //chan4_offset[...]: value index: 256 values
indexToLut .dim(0).set_stride(1); //chan4_offset[...]: value index: 256 values
}
Among others, I used the target feature 'no_assert' during generation (as can be seen in the output).
I then get the following output code:
module name=applyIccProfile, target=x86-64-windows-disable_llvm_loop_opt-mingw-no_asserts-no_bounds_query-no_runtime-sse41 {
func applyIccProfile(Lut, indexToLut, inputImageLine, outputImageLine) {
assert((reinterpret(outputImageLine.buffer) != (uint64)0), halide_error_buffer_argument_is_null("outputImageLine"))
assert((reinterpret(inputImageLine.buffer) != (uint64)0), halide_error_buffer_argument_is_null("inputImageLine"))
assert((reinterpret(indexToLut.buffer) != (uint64)0), halide_error_buffer_argument_is_null("indexToLut"))
assert((reinterpret(Lut.buffer) != (uint64)0), halide_error_buffer_argument_is_null("Lut"))
let Lut = _halide_buffer_get_host(Lut.buffer)
let Lut.min.0 = _halide_buffer_get_min(Lut.buffer, 0)
let Lut.extent.0 = _halide_buffer_get_extent(Lut.buffer, 0)
let Lut.stride.0 = _halide_buffer_get_stride(Lut.buffer, 0)
let indexToLut = _halide_buffer_get_host(indexToLut.buffer)
let indexToLut.min.0 = _halide_buffer_get_min(indexToLut.buffer, 0)
let indexToLut.extent.0 = _halide_buffer_get_extent(indexToLut.buffer, 0)
let indexToLut.stride.0 = _halide_buffer_get_stride(indexToLut.buffer, 0)
let inputImageLine = _halide_buffer_get_host(inputImageLine.buffer)
let inputImageLine.min.0 = _halide_buffer_get_min(inputImageLine.buffer, 0)
let inputImageLine.extent.0 = _halide_buffer_get_extent(inputImageLine.buffer, 0)
let inputImageLine.stride.0 = _halide_buffer_get_stride(inputImageLine.buffer, 0)
let outputImageLine = _halide_buffer_get_host(outputImageLine.buffer)
let outputImageLine.min.0 = _halide_buffer_get_min(outputImageLine.buffer, 0)
let outputImageLine.extent.0 = _halide_buffer_get_extent(outputImageLine.buffer, 0)
let outputImageLine.stride.0 = _halide_buffer_get_stride(outputImageLine.buffer, 0)
assert((Lut.stride.0 == 1), 0)
assert((Lut.min.0 == 0), 0)
assert((Lut.extent.0 == 17), 0)
assert((indexToLut.stride.0 == 1), 0)
assert((indexToLut.min.0 == 0), 0)
assert((indexToLut.extent.0 == 256), 0)
assert((inputImageLine.stride.0 == 1), 0)
assert((inputImageLine.min.0 == 0), 0)
assert((outputImageLine.stride.0 == 1), 0)
assert((outputImageLine.min.0 == 0), 0)
assert((outputImageLine.extent.0 == inputImageLine.extent.0), 0)
produce outputImageLine {
for (outputImageLine.s0.x, 0, inputImageLine.extent.0) {
outputImageLine[outputImageLine.s0.x] = Lut[max(min(indexToLut[int32(inputImageLine[outputImageLine.s0.x])], 17), 0)]
}
}
}
}
In the generated output a number of assertions are present
that check the dimensions of the buffers that are provided.
I know that these assertions are executed 'only' once for each call.
However, given the number of calls I would like to turn off these assertions,
because of the execution overhead.
So the questions are:
How can I turn off the assignments w.r.t. min/extent/stride for those that are already known (because these were set in the generator code)?
How can I turn off the generation of these assertions?
While asserts still show up in the Halide IR with no_asserts on, any remaining ones get stripped in the final lowering to LLVM IR. They just exist in the Halide IR because they let the Halide simplifier know that something can be assumed to be true after that point in the code, but they compile to a no-op.
With the asserts gone, LLVM will dead-code-eliminate the unnecessary assignments. I'd check the generated assembly rather than the Halide IR to be sure all of those checks are gone.
Related
I've recently started on HackerRank and I'm attempting "Sales by Match". I've arrived at a solution I'm content with in terms of exploiting Kotlin's function programming capabilities. However, I'm not getting the expected answer...
Problem summary:
Given an Array:
-> find and return the total number of pairs.
i.e:
input -> [10, 20, 20, 10, 10, 30, 50, 10, 20]
number of pairs -> 3
here is my code and some comments to explain it:
fun sockMerchant(n: Int, pile: Array<Int>): Int{
var count = 0
mutableMapOf<Int, Int>().withDefault { 0 }.apply {
// the [attempted] logic behind this piece of code here is
// that as we iterate through the list, the 'getOrPut()'
// function will return either the value for the given [key]
// or throw an exception if there is no such key
// in the map. However, since our map was created by
// [withDefault], the function resorts to its `defaultValue` <-> 0
// instead of throwing an exception.
for (e in values) {
// this simplifies our code and inserts a zero [+1] where needed.
// if (key exists)
// // return its associated value and MOD it:
// case: even -> increment counter
// else -> do nothing
// else if (key dne)
// // insert default value <-> [0] + 1
// ....
// ....
// ....
if (getOrPut(e, { getValue(e) + 1 } ) % 2 == 0) count++
}
}
return count
}
fun main(args: Array<String>) {
val scan = Scanner(System.`in`)
val n = scan.nextLine().trim().toInt()
val ar = scan.nextLine().split(" ").map{ it.trim().toInt() }.toTypedArray()
val result = sockMerchant(n, ar)
println(result)
}
--
Any help or tips would go a long way here:)
I was able to do this by grouping the numbers together, taking the resulting lists, and summing how many pairs each one contains:
fun sockMerchant(n: Int, pile: Array<Int>): Int =
pile.groupBy { it }.values.sumBy { it.size / 2 }
After we do pile.groupBy { it }, we have this structure:
{10=[10, 10, 10, 10], 20=[20, 20, 20], 30=[30], 50=[50]}
We take the values, and sum by each of their size dividing by 2. This will round half-pairs down to 0 and full pairs to 1 each.
Note: I am not entirely clear what the purpose of n is in this case.
I modified it a bit to be more easily testable, but here is the fixes :
import java.util.*
fun sockMerchant(n: Int, pile: Array<Int>): Int{
var count = 0
mutableMapOf<Int, Int>().withDefault { 0 }.apply {
// the [attempted] logic behind this piece of code here is
// that as we iterate through the list, the 'getOrPut()'
// function will return either the value for the given [key]
// or throw an exception if there is no such key
// in the map. However, since our map was created by
// [withDefault], the function resorts to its `defaultValue` <-> 0
// instead of throwing an exception.
for (e in pile) {
// this simplifies our code and inserts a zero [+1] where needed.
// if (key exists)
// // return its associated value and MOD it:
// case: even -> increment counter
// else -> do nothing
// else if (key dne)
// // insert default value <-> [0] + 1
// ....
// ....
// ....
println(e)
put(e, getValue(e) + 1)
if (getValue(e) % 2 == 0) count++
println(entries)
}
}
return count
}
val n = 5
val ar = "10 10 10 10 20 20 30 40".split(" ").map{ it.trim().toInt() }.toTypedArray()
val result = sockMerchant(n, ar)
println(result)
Output :
10
[10=1]
10
[10=2]
10
[10=3]
10
[10=4]
20
[10=4, 20=1]
20
[10=4, 20=2]
30
[10=4, 20=2, 30=1]
40
[10=4, 20=2, 30=1, 40=1]
3
Pair.kts:3:18: warning: parameter 'n' is never used
fun sockMerchant(n: Int, pile: Array<Int>): Int{
^
Process finished with exit code 0
Explanation :
You looped over "values", which is at start empty, so you never did anything with your code
Even when looping over pile, your incrementation logic didn't go above 1, so the condition was never satisfied and the count was never incremented.
But the main reasoning behind was correct.
I'm trying to solve this codewars kata, Square into Squares.
I'm passing most of the tests, but there are two inputs for which my algorithm exceeds the maximum call stack size.
I feel like I'm taking care of all the edge conditions, and I can't figure out what I'm missing.
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0) return result
else {
if (whatsLeft < 0 || num === 0) return null
else {
return decompose(num-1, whatsLeft - num * num, [num].concat(result)) || decompose(num-1, whatsLeft, result)
}
}
}
return decompose(n-1, n*n, [])
}
const resA = sumSquares(50) //[1,3,5,8,49]
console.log(resA)
const resB = sumSquares(7) //[2,3,6]
console.log(resB)
const resC = sumSquares(11) //[ 1, 2, 4, 10 ]
console.log(resC)
const res1 = sumSquares(90273)
console.log(res1)
const res2 = sumSquares(123456)
console.log(res2)
It looks like your code is correct, but has two problems: first, your call stack will eventually reach size "num" (which may be causing your failure for large inputs), and second, it may recompute the same values multiple times.
The first problem is easy to fix: you can skip num values which give a negative whatsLeft result. Like this:
while(num * num > whatsLeft) num = num - 1;
You can insert this after the first if statement. This also enables you to remove the check for negative whatsLeft. As a matter of style, I removed the else{} cases for your if statements after a return -- this reduces the indentation and (I think) makes the code easier to read. But that's just a matter of personal taste.
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0) return result;
while (num * num > whatsLeft) num -= 1;
if (num === 0) return null;
return decompose(num-1, whatsLeft - num * num, [num].concat(result)) || decompose(num-1, whatsLeft, result);
}
return decompose(n-1, n*n, []);
}
Your test cases run instantly for me with these changes, so the second problem (which would be solved by memoization) isn't necessary to address. I also tried submitting it on the codewars site, and with a little tweaking (the outer function needs to be called decompose, so both the outer and inner functions need renaming), all 113 test cases pass in 859ms.
#PaulHankin's answer offers good insight
Let's look at sumSquares (n) where n = 100000
decompose (1e5 - 1, 1e5 * 1e5, ...)
In the first frame,
num = 99999
whatsLeft = 10000000000
Which spawns
decompose (99999 - 1, 1e10 - 99999 * 99999, ...)
Where the second frame is
num = 99998
whatsLeft = 199999
And here's the problem: num * num above is significantly larger than whatsLeft and each time we recur to try a new num that first, we only decrease by -1 each frame. Without fixing anything, the next process spawned will be
decompose (99998 - 1, 199999 - 99998 * 99998, ...)
Where the third frame is
num = 99997
whatsLeft = -9999500005
See how whatsLeft is significantly negative? It means we'll have to decrease num by a lot before the next value doesn't cause whatsLeft to drop below zero
// [frame #4]
num = 99996
whatsLeft = -9999000017
// [frame #5]
num = 99995
whatsLeft = -9998800026
...
// [frame #99552]
num = 448
whatsLeft = -705
// [frame #99553]
num = 447
whatsLeft = 190
As we can see above, it would take almost 100000 frames just to guess the second digit of sumSquares (100000). This is exactly what Paul Hankin describes as your first problem.
We can also visualize it a little easer if we only look at decompose with num. Below, if a solution cannot be found, the stack will grow to size num and therefore cannot be used to compute solutions where num exceeds the stack limit
// imagine num = 100000
function decompose (num, ...) {
...
decompose (num - 1 ...) || decompose (num - 1, ...)
}
Paul's solution uses a while loop to decrement num using a loop until num is small enough. Another solution would involve calculating the next guess by finding the square root of the remaining whatsLeft
const sq = num * num
const next = whatsLeft - sq
const guess = Math.floor (Math.sqrt (next))
return decompose (guess, next, ...) || decompose (num - 1, whatsLeft, ...)
Now it can be used to calculate values where num is huge
console.log (sumSquares(123456))
// [ 1, 2, 7, 29, 496, 123455 ]
But notice there's a bug for certain inputs. The squares of the solution still sum to the correct amount, but it's allowing some numbers to be repeated
console.log (sumSquares(50))
// [ 1, 1, 4, 9, 49 ]
To enforce the strictly increasing requirement, we have to ensure that a calculated guess is still lower than the previous. We can do that using Math.min
const guess = Math.floor (Math.sqrt (next))
const guess = Math.min (num - 1, Math.floor (Math.sqrt (next)))
Now the bug is fixed
console.log (sumSquares(50))
// [ 1, 1, 4, 9, 49 ]
// [ 1, 3, 5, 8, 49 ]
Full program demonstration
function sumSquares (n) {
function decompose (num, whatsLeft, result) {
if (whatsLeft === 0)
return result;
if (whatsLeft < 0 || num === 0)
return null;
const sq = num * num
const next = whatsLeft - sq
const guess = Math.min (num - 1, Math.floor (Math.sqrt (next)))
return decompose(guess, next, [num].concat(result)) || decompose(num-1, whatsLeft, result);
}
return decompose(n-1, n*n, []);
}
console.log (sumSquares(50))
// [ 1, 3, 5, 8, 49 ]
console.log (sumSquares(123456))
// [ 1, 2, 7, 29, 496, 123455 ]
When trying to run the sample code below (similar to a look up table), it always generates the following error message: "The pure definition of Function 'out' calls function 'color' in an unbounded way in dimension 0".
RDom r(0, 10, 0, 10);
Func label, color, out;
Var x,y,c;
label(x,y) = 0;
label(r.x,r.y) = 1;
color(c) = 0;
color(label(r.x,r.y)) = 255;
out(x,y) = color(label(x,y));
out.realize(10,10);
Before calling realize, I have tried to statically set bound, like below, without success.
color.bound(c,0,10);
label.bound(x,0,10).bound(y,0,10);
out.bound(x,0,10).bound(y,0,10);
I also looked at the histogram examples, but they are a bit different.
Is this some kind of restrictions in Halide?
Halide prevents any out of bounds access (and decides what to compute) by analyzing the range of the values you pass as arguments to a Func. If those values are unbounded, it can't do that. The way to make them bounded is with clamp:
out(x, y) = color(clamp(label(x, y), 0, 9));
In this case, the reason it's unbounded is that label has an update definition, which makes the analysis give up. If you wrote label like this instead:
label(x, y) = select(x >= 0 && x < 10 && y >= 0 && y < 10, 1, 0);
Then you wouldn't need the clamp.
A vampire number is defined here https://en.wikipedia.org/wiki/Vampire_number. A number V is a vampire number if:
It can be expressed as X*Y such that X and Y have N/2 digits each where N is the number of digits in V
Both X & Y should not have trailing zeros
X & Y together should have the same digits as V
I came up with a solution,
strV = sort(toString(V))
for factor <- pow(10, N/2) to sqrt(V)
if factor divides V
X <- factor
Y <- V/factor
if X and Y have trailing zeros
continue
checkStr = sort(toString(X) + toString(Y))
if checkStr equals strV return true
Another possible solution is to permute the string represented by V and split it into half and check if its a vampire number. Which one is the best way to do so?
The algorithm I propose here will not go through all permutations of digits. It will eliminate possibilities as fast as possible so that only a fraction of permutations will actually be tested.
Algorithm explained by example
Here is how it works based on example number 125460. If you are fine with reading the code directly, then you can skip this (long) part:
At first the two fangs (i.e. vampire factors) are obviously not known, and the problem can be represented as follows:
?**
X ?**
-------
=125460
For the left most digit of the first factor (marked with ?) we could choose any of the digits 0,1,2,5,4, or 6. But on closer analysis 0 would not be a viable possibility, as the product would never reach more than a 5-digit number. So it would be a waste of time to go through all permutations of digits that start with a zero.
For the left most digit of the second factor (also marked with ?), the same is true. However, when looking at the combinations, we can again filter out some pairs that cannot contribute to reaching the target product. For instance, this combination should be discarded:
1**
X 2**
-------
=125460
The greatest number that can be achieved with these digits is 199x299 = 59501 (ignoring the fact that we don't even have a 9), which is not even half of the desired number. So we should reject the combination (1, 2). For the same reason, the pair (1, 5) can be discarded for taking these positions. Similarly, the pairs (4, 5), (4, 6), and (5, 6) can be rejected as well, because they yield a too large product (>= 200000). I will call this kind of a test -- where it is determined whether the target number is within reach for a certain chosen digit pair, the "range test".
At this stage there is no difference between the first and the second fang, so we should also not have to investigate pairs where the second digit is smaller than the first, because they mirror a pair that would already have been investigated (or rejected).
So of all the possible pairs that could take up this first position (there are 30 possibilities to take 2 digits from a set of 6 digits), only the following 4 need to be investigated:
(1, 6), (2, 4), (2, 5), (2, 6)
In a more elaborate notation this means we are limiting the search to these number patterns:
1** 2** 2** 2**
X 6** X 4** X 5** X 6**
------- ------- ------- -------
=125460 =125460 =125460 =125460
A B C D
It is clear that this reduction of possibilities before even looking at the other positions greatly reduces the search tree.
The algorithm will take each of these 4 possibilities in order, and for each will check the possibilities for the next digit position. So first configuration A is analysed:
1?*
X 6?*
-------
=125460
The pairs that are available for the ?-marked positions are these 12:
(0, 2), (0, 4), (0, 5)
(2, 0), (2, 4), (2, 5)
(4, 0), (4, 2), (4, 5)
(5, 0), (5, 2), (5, 4)
Again, we can eliminate pairs by applying the range test. Let's take for instance the pair (5, 4). This would mean we had factors 15* and 64* (where * is an unknown digit at this point). The product of these two will be maximised with 159 * 649, i.e. 103191 (again ignoring the fact we do not even have a 9 available): this is too low for reaching the target, so this pair can be ignored. By further applying the range test, all these 12 pairs can be discarded, and so the search within configuration A stops here: there is no solution there.
Then the algorithm moves to configuration B:
2?*
X 4?*
-------
=125460
Again, the range test is applied to the possible pairs for the second position, and again it turns out none of these pairs passes the test: for instance (5, 6) can never represent a greater product than 259 * 469 = 121471, which is (only just) too small.
Then the algorithm moves to option C:
2?*
X 5?*
-------
=125460
Of all 12 possible pairs, only the following survive the range test: (4, 0), (4, 1), (6, 0), (6, 1). So now we have the following second-level configurations:
24* 24* 26* 26*
X 50* X 51* X 50* X 51*
------- ------- ------- -------
=125460 =125460 =125460 =125460
Ca Cb Cc Cd
In configuration Ca, there is no pair that passes the range test.
In configuration Cb, the pair (6, 0) passes, and leads to a solution:
246
X 510
-------
=125460
At this point the algorithm stops searching. The outcome is clear. In total the number of configurations looked at is very small compared to a brute force permutation checking algorithm. Here is a visualisation of the search tree:
*-+-- (1, 6)
|
+-- (2, 4)
|
+-- (2, 5) -+-- (4, 0)
| |
| +-- (4, 1) ---- (6, 0) = success: 246 * 510
/ /
| +-- (6, 0)
| |
| +-- (6, 1)
|
+-- (2, 6) ---- (0, 1) ---- (4, 5) = success: 204 * 615
The variants below / are only for showing what else the algorithm would have done, if there had not been a solution found. But in this actual case, that part of the search tree was actually never followed.
I have no clear idea of the time complexity, but it seems to run quite well for larger numbers, showing that the elimination of digits at an early stage makes the width of the search tree quite narrow.
Here is a live JavaScript implementation, which also runs some test cases when it it is activated (and it has a few other optimisations -- see code comments).
/*
Function: vampireFangs
Arguments:
vampire: number to factorise into two fangs, if possible.
Return value:
Array with two fangs if indeed the argument is a vampire number.
Otherwise false (not a vampire number) or null (argument too large to
compute)
*/
function vampireFangs(vampire) {
/* Function recurse: for the recursive part of the algorithm.
prevA, prevB: partial, potential fangs based on left-most digits of the given
number
counts: array of ten numbers representing the occurrence of still
available digits
divider: power of 100, is divided by 100 each next level in the search tree.
Determines the number of right-most digits of the given number that
are ignored at first in the algorithm. They will be considered in
deeper levels of recursion.
*/
function recurse(vampire, prevA, prevB, counts, divider) {
if (divider < 1) { // end of recursion
// Product of fangs must equal original number and fangs must not both
// end with a 0.
return prevA * prevB === vampire && (prevA % 10 + prevB % 10 > 0)
? [prevA, prevB] // Solution found
: false; // It's not a solution
}
// Get left-most digits (multiple of 2) of potential vampire number
var v = Math.floor(vampire/divider);
// Shift decimal digits of partial fangs to the left to make room for
// the next digits
prevA *= 10;
prevB *= 10;
// Calculate the min/max A digit that can potentially contribute to a
// solution
var minDigA = Math.floor(v / (prevB + 10)) - prevA;
var maxDigA = prevB ? Math.floor((v + 1) / prevB) - prevA : 9;
if (maxDigA > 9) maxDigA = 9;
for (var digA = minDigA; digA <= maxDigA; digA++) {
if (!counts[digA]) continue; // this digit is not available
var fangA = prevA + digA;
counts[digA]--;
// Calculate the min/max B digit that can potentially contribute to
// a solution
var minDigB = Math.floor(v / (fangA + 1)) - prevB;
var maxDigB = fangA ? (v + 1) / fangA - prevB : 9;
// Don't search mirrored A-B digits when both fangs are equal until now.
if (prevA === prevB && digA > minDigB) minDigB = digA;
if (maxDigB > 9) maxDigB = 9;
for (var digB = minDigB; digB <= Math.min(maxDigB, 9); digB++) {
if (!counts[digB]) continue; // this digit is not available
var fangB = prevB + digB;
counts[digB]--;
// Recurse by considering the next two digits of the potential
// vampire number, for finding the next digits to append to
// both partial fangs.
var result = recurse(vampire, fangA, fangB, counts, divider / 100);
// When one solution is found: stop searching & exit search tree.
if (result) return result; // solution found
// Restore counts
counts[digB]++;
}
counts[digA]++;
}
}
// Validate argument
if (typeof vampire !== 'number') return false;
if (vampire < 0 || vampire % 1 !== 0) return false; // not positive and integer
if (vampire > 9007199254740991) return null; // beyond JavaScript precision
var digits = vampire.toString(10).split('').map(Number);
// A vampire number has an even number of digits
if (!digits.length || digits.length % 2 > 0) return false;
// Register per digit (0..9) the frequency of that digit in the argument
var counts = [0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < digits.length; i++) {
counts[digits[i]]++;
}
return recurse(vampire, 0, 0, counts, Math.pow(10, digits.length - 2));
}
function Timer() {
function now() { // try performance object, else use Date
return performance ? performance.now() : new Date().getTime();
}
var start = now();
this.spent = function () { return Math.round(now() - start); }
}
// I/O
var button = document.querySelector('button');
var input = document.querySelector('input');
var output = document.querySelector('pre');
button.onclick = function () {
var str = input.value;
// Convert to number
var vampire = parseInt(str);
// Measure performance
var timer = new Timer();
// Input must be valid number
var result = vampire.toString(10) !== str ? null
: vampireFangs(vampire);
output.textContent = (result
? 'Vampire number. Fangs are: ' + result.join(', ')
: result === null
? 'Input is not an integer or too large for JavaScript'
: 'Not a vampire number')
+ '\nTime spent: ' + timer.spent() + 'ms';
}
// Tests (numbers taken from wiki page)
var tests = [
// Negative test cases:
[1, 999, 126000, 1023],
// Positive test cases:
[1260, 1395, 1435, 1530, 1827, 2187, 6880,
102510, 104260, 105210, 105264, 105750, 108135,
110758, 115672, 116725, 117067, 118440,
120600, 123354, 124483, 125248, 125433, 125460, 125500,
13078260,
16758243290880,
24959017348650]
];
tests.forEach(function (vampires, shouldBeVampire) {
vampires.forEach(function (vampire) {
var isVampire = vampireFangs(vampire);
if (!isVampire !== !shouldBeVampire) {
output.textContent = 'Unexpected: vampireFangs('
+ vampire + ') returns ' + JSON.stringify(isVampire);
throw 'Test failed';
}
});
});
output.textContent = 'All tests passed.';
N: <input value="1047527295416280"><button>Vampire Check</button>
<pre></pre>
As JavaScript uses 64 bit floating point representation, the above snippet only accepts to numbers up to 253-1. Above that limit there would be loss of precision and consequently unreliable results.
As Python does not have such limitation, I also put a Python implementation on eval.in. That site has a limitation on execution times, so you'd have to run it elsewhere if that becomes an issue.
In pseudocode:
if digitcount is odd return false
if digitcount is 2 return false
for A = each permutation of length digitcount/2 selected from all the digits,
for B = each permutation of the remaining digits,
if either A or B starts with a zero, continue
if both A and B end in a zero, continue
if A*B == the number, return true
There are a number of optimizations that could still be performed here, mostly in terms of ensuring that each possible pair of factors is tried only once. In other words, how to best check for repeating digits when selecting permutations?
But that's the gist of the algorithm I would use.
P.S.: You're not looking for primes, so why use a primality test? You just care about whether these are vampire numbers; there are only a very few possible factors. No need to check all the numbers up to sqrt(number).
Here are some suggestions:
First a simple improvement: if the number of digits is < 4 or odd return false (or if v is negative too).
You don't need to sort v, it is enough to count how many times each digit occurs O(n).
You don't have to check each number, only the combinations that are possible with the digits. This could be done by backtracking and significantly reduces the amount of numbers that have to be checked.
The final sort to check if all digits were used isn't needed either, just add up the used digits of both numbers and compare with the occurences in v.
Here is the code for a JS-like language with integers that never overflow, the V parameter is an integer string without leading 0s:
Edit: As it turns out the code is not only JS-like, but valid JS code and it had no problem to decide that 1047527295416280 is indeed a vampire number (jsfiddle).
var V, v, isVmp, digits, len;
function isVampire(numberString) {
V = numberString;
if (V.length < 4 || V.length % 2 == 1 )
return false;
v = parseInt(V);
if (v < 0)
return false;
digits = countDigits(V);
len = V.length / 2;
isVmp = false;
checkNumbers();
return isVmp;
}
function countDigits(s) {
var offset = "0".charCodeAt(0);
var ret = [0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < s.length; i++)
ret[s.charCodeAt(i) - offset]++;
return ret;
}
function checkNumbers(number, depth) {
if (isVmp)
return;
if (typeof number == 'undefined') {
for (var i = 1; i < 10; i++) {
if (digits[i] > 0) {
digits[i]--;
checkNumbers(i, len - 1);
digits[i]++;
}
}
} else if (depth == 0) {
if (v % number == 0) {
var b = v / number;
if (number % 10 != 0 || b % 10 != 0) {
var d = countDigits('' + b);
if (d[0] == digits[0] && d[1] == digits[1] && d[2] == digits[2] &&
d[3] == digits[3] && d[4] == digits[4] && d[5] == digits[5] &&
d[6] == digits[6] && d[7] == digits[7] && d[8] == digits[8] &&
d[9] == digits[9])
isVmp = true;
}
}
} else {
for (var i = 0; i < 10; i++) {
if (digits[i] > 0) {
digits[i]--;
checkNumbers(number * 10 + i, depth - 1);
digits[i]++;
}
}
}
}
I was asked this question in a interview, so I don't want the solution, just the guidance regarding how to approach it.
You have been given two numbers low and high. And a random generator which generates 0 and 1. I have to generate a number between low and high using that function.
I can get difference between the two numbers and somehow try to generate a number using bit manipulation. But I am not able to figure out how to do that?
You can do:
range = high - low
find n such that 2^n-1 < range <= 2^n
run the random generator n times to generate an int thanks to its binary representation. Something like 010011010 (= 154 in decimal)
add the obtained number to low to get your final number!
Here's a basic bit-by-bit comparison algorithm that gives a random number between low and high, using a random-bit function:
Decrease high by 1 and increase low by 1 (in case the random bits introduced later all equal those in high or low).
Create booleans high_dec and low_inc to store whether at least one 1 in high has been changed into 0, and at least one 0 in low has been changed into 1, and set both of them to false (these will help avoid the result going out of range).
Compare high and low bit-by-bit from MSB to LSB with these cases:
If you find high:1 and low:1 then store a 1 if low_inc=false or store a random bit otherwise (and update high_dec as necessary).
If you find high:1 and low:0 then store a random bit (and update high_dec or low_inc as necessary).
If you find high:0 and low:1 then store a 0 if high_dec=false or store a 1 if low_inc=false or store a random bit otherwise.
If you find high:0 and low:0 then store a 0 if high_dec=false or store a random bit otherwise (and update low_inc as necessary).
Note that the distribution of the random numbers is only uniform if the lowest possible result is a power of 2, and the range is a power of 2. In all cases the whole range is used, but there may be an emphasis on values near the beginning or end of the range.
function between(a, b) {
var lo = (a + 1).toString(2).split(''), // conversion to bit array because
hi = (b - 1).toString(2).split(''), // there is no bit manipulation in JS
lc = false, // low changed
hc = false, // high changed
result = [];
while (lo.length < hi.length) lo.unshift(0); // add leading zeros to low
for (var i = 0; i < hi.length; i++) { // iterate over bits, msb to lsb
var bit = Math.round(Math.random()); // random bit generator
if (hi[i] == 1) {
if (lo[i] == 1) { // case hi:1 lo:1
if (lc == false) bit = 1
else if (bit == 0) hc = true;
} else { // case hi:1 lo:0
if (bit == 0) hc = true
else lc = true;
}
} else {
if (lo[i] == 1) { // case hi:0 lo:1
if (hc == false) bit = 0
else if (lc == false) bit = 1;
} else { // case hi:0 lo:0
if (hc == false) bit = 0
else if (bit == 1) lc = true;
}
}
result.push(bit);
}
return parseInt(result.join(''), 2); // convert bit array to integer
}
document.write(between(999999, 1000100) + "<BR>");