Related
I created a bloom filter using murmur3, blake2b, and Kirsch-Mitzenmacher-optimization, as described in the second answer to this question: Which hash functions to use in a Bloom filter
However, when I was testing it, the bloom filter constantly had a much higher error rate than I was expecting.
Here is the code I used to generate the bloom filters:
public class BloomFilter {
private BitSet filter;
private int size;
private int hfNum;
private int prime;
private double fp = 232000; //One false positive every fp items
public BloomFilter(int count) {
size = (int)Math.ceil(Math.ceil(((double)-count) * Math.log(1/fp))/(Math.pow(Math.log(2),2)));
hfNum = (int)Math.ceil(((this.size / count) * Math.log(2)));
//size = (int)Math.ceil((hfNum * count) / Math.log(2.0));
filter = new BitSet(size);
System.out.println("Initialized filter with " + size + " positions and " + hfNum + " hash functions.");
}
public BloomFilter extraSecure(int count) {
return new BloomFilter(count, true);
}
private BloomFilter(int count, boolean x) {
size = (int)Math.ceil((((double)-count) * Math.log(1/fp))/(Math.pow(Math.log(2),2)));
hfNum = (int)Math.ceil(((this.size / count) * Math.log(2)));
prime = findPrime();
size = prime * hfNum;
filter = new BitSet(prime * hfNum);
System.out.println("Initialized filter with " + size + " positions and " + hfNum + " hash functions.");
}
public void add(String in) {
filter.set(getMurmur(in), true);
filter.set(getBlake(in), true);
if(this.hfNum > 2) {
for(int i = 3; i <= (hfNum); i++) {
filter.set(getHash(in, i));
}
}
}
public boolean check(String in) {
if(!filter.get(getMurmur(in)) || !filter.get(getBlake(in))) {
return false;
}
for(int i = 3; i <= hfNum; i++) {
if(!filter.get(getHash(in, i))) {
return false;
}
}
return true;
}
private int getMurmur(String in) {
int temp = murmur(in) % (size);
if(temp < 0) {
temp = temp * -1;
}
return temp;
}
private int getBlake(String in) {
int temp = new BigInteger(blake256(in), 16).intValue() % (size);
if(temp < 0) {
temp = temp * -1;
}
return temp;
}
private int getHash(String in, int i) {
int temp = ((getMurmur(in)) + (i * getBlake(in))) % size;
return temp;
}
private int findPrime() {
int temp;
int test = size;
while((test * hfNum) > size ) {
temp = test - 1;
while(!isPrime(temp)) {
temp--;
}
test = temp;
}
if((test * hfNum) < this.size) {
test++;
while(!isPrime(test)) {
test++;
}
}
return test;
}
private static boolean isPrime(int num) {
if (num < 2) return false;
if (num == 2) return true;
if (num % 2 == 0) return false;
for (int i = 3; i * i <= num; i += 2)
if (num % i == 0) return false;
return true;
}
#Override
public String toString() {
final StringBuilder buffer = new StringBuilder(size);
IntStream.range(0, size).mapToObj(i -> filter.get(i) ? '1' : '0').forEach(buffer::append);
return buffer.toString();
}
}
Here is the code I'm using to test it:
public static void main(String[] args) throws Exception {
int z = 0;
int times = 10;
while(z < times) {
z++;
System.out.print("\r");
System.out.print(z);
BloomFilter test = new BloomFilter(4000);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
for(int i = 0; i < 4000; i++) {
test.add(blake256(Integer.toString(random.nextInt())));
}
int temp = 0;
int count = 1;
while(!test.check(blake512(Integer.toString(temp)))) {
temp = random.nextInt();
count++;
}
if(z == (times)) {
Files.write(Paths.get("counts.txt"), (Integer.toString(count)).getBytes(), StandardOpenOption.APPEND);
}else {
Files.write(Paths.get("counts.txt"), (Integer.toString(count) + ",").getBytes(), StandardOpenOption.APPEND);
}
if(z == 1) {
Files.write(Paths.get("counts.txt"), (Integer.toString(count) + ",").getBytes());
}
}
}
I expect to get a value relatively close to the fp variable in the bloom filter class, but instead I frequently get half that. Anyone know what I'm doing wrong, or if this is normal?
EDIT: To show what I mean by high error rates, when I run the code on a filter initialized with count 4000 and fp 232000, this was the output in terms of how many numbers the filter had to run through before it found a false positive:
158852,354114,48563,76875,156033,82506,61294,2529,82008,32624
This was generated using the extraSecure() method for initialization, and repeated 10 times to generate these 10 numbers; all but one of them took less than 232000 generated values to find a false positive. The average of the 10 is about 105540, and that's common no matter how many times I repeat this test.
Looking at the values it found, the fact that it found a false positive after only generating 2529 numbers is a huge issue for me, considering I'm adding 4000 data points.
I'm afraid I don't know where the bug is, but you can simplify a lot. You don't actually need prime size, you don't need SecureRandom, BigInteger, and modulo. All you need is a good 64 bit hash (seeded if possible, for example murmur):
long bits = (long) (entryCount * bitsPerKey);
int arraySize = (int) ((bits + 63) / 64);
long[] data = new long[arraySize];
int k = getBestK(bitsPerKey);
void add(long key) {
long hash = hash64(key, seed);
int a = (int) (hash >>> 32);
int b = (int) hash;
for (int i = 0; i < k; i++) {
data[reduce(a, arraySize)] |= 1L << index;
a += b;
}
}
boolean mayContain(long key) {
long hash = hash64(key, seed);
int a = (int) (hash >>> 32);
int b = (int) hash;
for (int i = 0; i < k; i++) {
if ((data[reduce(a, arraySize)] & 1L << a) == 0) {
return false;
}
a += b;
}
return true;
}
static int reduce(int hash, int n) {
// http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
return (int) (((hash & 0xffffffffL) * n) >>> 32);
}
static int getBestK(double bitsPerKey) {
return Math.max(1, (int) Math.round(bitsPerKey * Math.log(2)));
}
Turns out the issue was that the answer on the other page wasn't completely correct, and neither was the comment below it.
The comment said:
in the paper hash_i = hash1 + i x hash2 % p, where p is a prime, hash1 and hash2 is within range of [0, p-1], and the bitset consists k * p bits.
However, looking at the paper reveals that while all the hashes are mod p, each hash function is assigned a subset of the total bitset, which I understood to mean hash1 mod p would determine a value for indices 0 through p, hash2 mod p would determine a value for indices p through 2*p, and so on and so forth until the k value chosen for the bitset is reached.
I'm not 100% sure if adding this will fix my code, but it's worth a try. I'll update this if it works.
UPDATE: Didn't help. I'm looking into what else may be causing this problem.
A week ago I was given a challenge from my professor to make a program that has three operations for large numbers, given as as strings. I could only pass five of the ten test cases and got an A anyway, but I still want to know what you guys would do for this problem, as far as programming techniques or an approach I didn't think of..
You are given a String representation of a number that is up to 309 digits long. You can preform three operations:
1) Add 1 to the number
2) Subtract 1
3) Divide by 2
the purpose of the program is to find the shortest path, or smallest amount of operations that can be performed on this number so that the result is 1.
ex: given "11"
1 -> 2 -> 3 -> 6 -> 12 -> 11
result: 5 steps
I had two approaches that didn't work 100%:
1: start from one or the number itself and recursively step through each possible answer until number is reached within a maximum number of steps (eg. 11, 20).
2: define all possible answers with the help of a 2-d boolean array with all possible permutaions, then step through the possible movesets one by one. this array works as a map conceptually.
Both of these approaches had limited success, wether i encountered a stackoverflow error or just ran out of memory with my large arrays. This forced me to limit the number of steps so the code could function somewhat successfully. What would be your approach?
EDIT 1:30pm
attempt 1(sorry, it has been edited severely, hence why it wasn't shown earlier...):
import java.math.BigInteger;
import java.util.Scanner;
public class Answer {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str;
Scanner sc = new Scanner(System.in);
while (true) {
str = sc.nextLine();
System.out.println(answer(str));
}
// System.out.println(answer("15"));
}
private static BigInteger minNumOfJumps;
private static BigInteger big2 = BigInteger.valueOf(2);
/** smallest Number of jumps reached so far */
private static BigInteger smallestAmountOfJumps;
public static int answer(String string) {
// TODO Auto-generated method stub
BigInteger src = new BigInteger(string);
// BigInteger currentJump = BigInteger.ZERO; //used to initialize the
// nodes
// minNumOfJumps = src.divide(big2).add(BigInteger.ONE); //this must
// execute...
minNumOfJumps = new BigInteger("14"); // this must execute...
smallestAmountOfJumps = new BigInteger(minNumOfJumps.toString());
// System.out.println(minNumOfJumps);
Node n = new Node(src); // ...before this
return Integer.parseInt(getSmallestAmountOfJumps().toString());
// System.out.println(n.getSmallestAmountOfJumps().toString());
}
public static BigInteger getBig2() {
return big2;
}
public static void setBig2(BigInteger big2) {
Answer.big2 = big2;
}
public static BigInteger getMinNumOfJumps() {
return minNumOfJumps;
}
public static BigInteger getSmallestAmountOfJumps() {
return smallestAmountOfJumps;
}
public static void setSmallestAmountOfJumps(String smallestAmountOfJumps) {
Answer.smallestAmountOfJumps = new BigInteger(smallestAmountOfJumps);
}
}
/*
* I have never made a shortest path algorithm before, so i hope this is toyour
* satisfaction
*/
class Node {
/** number of nodes per creation */
private static final int NUMBER_OF_NODES_PER_NODE = 3;
/** if this number is exceeded, no more jumps are necessary. */ // SAVE THAT
// THINKING
// JUICE!
// private static BigInteger POSSIBLE_MINIMUM_NUMBER_OF_JUMPS;
private static boolean lastTransformWasRemoveOne;
private static boolean lastTransformWasAddOne;
// if one is the given value(src)
// private boolean isOneReached;
/** if the current path isn't valid */
// private boolean threadIsBroken;
// value passed during creation
private BigInteger src;
// current jump
private BigInteger currentJump;
// all possible transformations during next jump
// private Node[] path;
private Node(BigInteger src, BigInteger jump) {
currentJump = jump;
this.src = src;
// POSSIBLE_MINIMUM_NUMBER_OF_JUMPS = Answer.getMinNumOfJumps();
// this.path = new Node[NUMBER_OF_NODES_PER_NODE];
// 0 = remove | 1 = add | 2 = divide
for (int i = 0; i < NUMBER_OF_NODES_PER_NODE; i++) {
// System.out.println("i: " + i);
// System.out.println("src: " + src);
// System.out.println("compare: " +
// currentJump.compareTo(smallestAmountOfJumps));
// System.out.println("currentJump: " + currentJump);
// System.out.println("smallestAmountOfJumps: " +
// smallestAmountOfJumps);
if (src.compareTo(BigInteger.ONE) == 0) {
if (currentJump.subtract(Answer.getSmallestAmountOfJumps()).compareTo(BigInteger.ZERO) == -1) {
Answer.setSmallestAmountOfJumps(currentJump.toString());
// this below may break the code, but i think it fits with
// the logic
break;
}
} else if (i == 0) { // remove 1
// System.out.println(lastTransformWasAddOne);
// System.out.println("compare: " +
// currentJump.compareTo(smallestAmountOfJumps));
// System.out.println("currentJump: " + currentJump);
// System.out.println("smallestAmountOfJumps: " +
// smallestAmountOfJumps);
if (!lastTransformWasAddOne && currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) {
lastTransformWasRemoveOne = true;
Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
}
} else if (i == 1 && !lastTransformWasRemoveOne
&& currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) { // add
// 1
lastTransformWasAddOne = true;
Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
} else if (src.mod(Answer.getBig2()) == BigInteger.ZERO
&& currentJump.compareTo(Answer.getSmallestAmountOfJumps()) < 0) { // divide
// by
// 2
lastTransformWasRemoveOne = false;
lastTransformWasAddOne = false;
Node n = new Node(transform(i), currentJump.add(BigInteger.ONE));
} else if (currentJump.compareTo(Answer.getSmallestAmountOfJumps()) == 0)
break;
}
}
private BigInteger transform(int i) {
// TODO Auto-generated method stub
return (i == 0) ? src.subtract(BigInteger.ONE)
: (i == 1) ? src.add(BigInteger.ONE) : (i == 2) ? src.divide(Answer.getBig2()) : BigInteger.ZERO;
}
/**
* To be called once and only once.
*/
public Node(BigInteger src) {
this(src, BigInteger.ZERO);
}
}`
and this is another attempt:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class AnswerLessProficient {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str;
Scanner sc = new Scanner(System.in);
while (true) {
str = sc.nextLine();
System.out.println(answer(str));
}
// System.out.println(answer("15"));
}
private static boolean notFirstCall;
private static boolean pathIsSet;
private static boolean[][] boolArray;
private static final String ZERO = "0";
private static final BigInteger TWO = BigInteger.ONE.add(BigInteger.ONE);
private static int maximumSteps;
private static int maximumPermutations;
private static ArrayList<byte[]> listOfPaths;
private static Set<byte[]> setOfPaths;
// private static final int maximumPermutations = halfMaximumPermutations *
// 2;
// private static byte[][] path;
private static BigInteger src;
private static int steps;
private static BigInteger tempSrc;
private static byte[] tempPath;
// private static boolean followThePathsWithAlternateRoutesWasCalledOnce;
public static int answer(String s) {
// listOfPaths = new ArrayList<>();
src = new BigInteger(s);
tempSrc = new BigInteger(s);
maximumSteps = 9;
steps = maximumSteps;
maximumPermutations = (int) Math.pow(2, maximumSteps);
if (!notFirstCall) {
tempPath = new byte[maximumSteps];
setOfPaths = new HashSet<>();
int mercyVar = (int) Math.pow(2, maximumSteps);
// path = new byte[maximumPermutations][maximumSteps];
// boolArray = new boolean[maximumPermutations][maximumSteps];
for (int i = 0; i < mercyVar; i++) {
listOfPaths = new ArrayList<>();
String bin = (Integer.toBinaryString(i));
while (bin.length() < maximumSteps)
bin = (ZERO + bin);
char[] chars = bin.toString().toCharArray();
byte[] tempPath = new byte[maximumSteps];
for (int j = 0; j < maximumSteps; j++) {
// if (!pathIsSet)
// path[j] = -1;
if (chars[j] == '0') {
tempPath[j] = 2;
// path[i][j] = 2;
// boolArray[i][j] = true;
} else {
tempPath[j] = -1;
// path[i][j] = -1;
}
}
//System.out.println(Arrays.toString(tempPath));
listOfPaths.add(tempPath);
setOfPaths.add(tempPath);
findAltRoute(listOfPaths.size() - 1, maximumSteps - 1);
}
/*
* for (int i = mercyVar, j = 0; i < maximumPermutations; i++, j++)
* { for (int k = 0; k < maximumSteps; k++) { if (path[j][k] == -1)
* { path[i][k] = 1; } else { path[i][k] = 2; } } }
*/
// for (byte[] bs : setOfPaths) {
// System.out.println(Arrays.toString(bs));
// }
/*
* for (int i = maximumSteps - 1, k = 0; i >= 0 &&
* tempSrc.compareTo(BigInteger.ZERO) > 0; i--, k++) { if
* (tempSrc.compareTo(BigInteger.ONE) <= 0) if (k < steps) { steps =
* k; maximumSteps = steps; System.out.println(Arrays.toString(bs));
* break; } else break; if (bs[i] == 2 && tempSrc.mod(TWO) !=
* BigInteger.ZERO) break; tempSrc = transform(tempSrc, bs[i]); }
* tempSrc = src.add(BigInteger.ZERO);
*/
// }
// System.out.println(bin);
/*
* for (int j = 0; j < maximumSteps && i >= halfMaximumPermutations;
* j++) { // if (!pathIsSet) // path[j] = -1; if (chars[j + 1] ==
* '0') { path[i][j] = 2; // boolArray[i][j] = true; } else {
* path[i][j] = 1; } }
*/
// System.out.println(bin);
// System.out.println(Arrays.toString(path[i]));
// pathIsSet = true;
notFirstCall = true;
}
justFollowThePath();
// System.out.println(Arrays.toString(path[0]));
// System.out.println
// (Arrays.toString(path[(int) (maximumPermutations/2)-1]));
// System.out.println(Arrays.toString(path[maximumPermutations-1]));
/**
* 561-508-2204 george rubio debt forgiveness; 305-709-8255
*/
// for (int i = 0; i < maximumPermutations; i++) {
// followThePathsWithAlternateRoutes(path[i], maximumSteps - 1);
// }
// followThePathsWithAlternateRoutesWasCalledOnce = false;
/*
* for (int i = 0; i < maximumPermutations; i++) { for (int k = 0; k <
* maximumSteps; k++) {
*
* }
*
* for (int k = maximumSteps - 1; k > 0; k--) {
*
* } }
*/
// for (boolean[] bs : boolArray) {
// System.out.println(Arrays.toString(bs));
// }
// System.out.println(Arrays.toString(boolArray[maximumPermutations -
// 1]));
// System.out.println(Arrays.toString(path));
return steps;
}
private static void findAltRoute(int listIndex, int startingSearchIndex) {
if (listOfPaths.get(listIndex)[startingSearchIndex] == -1) {
// followThePathsWithAlternateRoutesWasCalledOnce = true;
// recurAlt(tempPath, maximumSteps - 1, maximumSteps - 1, (byte) 1,
// maximumSteps - 1);
for (int i = startingSearchIndex - 1; i >= 0; i--) {
if (listOfPaths.get(listIndex)[i] == 2) {
returnAltRoute(listIndex, i + 1, startingSearchIndex, (byte) 1, i);
findAltRoute(listIndex + 1, i);
return;
}
else if (i == 0) {
returnAltRoute(listIndex, i, startingSearchIndex, (byte) 1);
return;
}
}
}
for (int i = startingSearchIndex - 1; i >= 0; i--) {
if (listOfPaths.get(listIndex)[i] == -1 && listOfPaths.get(listIndex)[i + 1] == 2) {
if (i != 0) {
for (int k = i - 1; k >= 0; k--) {
if (listOfPaths.get(listIndex)[k] == 2 && listOfPaths.get(listIndex)[k + 1] == -1) {
// recurAlt(tempPath, i, k + 1, (byte) 1, k);
returnAltRoute(listIndex, k + 1, i, (byte) 1, k);
findAltRoute(listIndex, i);
}
// returnAltRoute(listIndex, 0, i, (byte)1);
// return;
}
} else {
returnAltRoute(listIndex, 0, i, (byte) 1);
return;
}
}
}
}
private static void returnAltRoute(int listIndex, int tempStart, int tempEnd, byte adjust, int returnSearchInt) {
byte[] tempPath = new byte[listOfPaths.get(listIndex).length];
for (int i = maximumSteps - 1; i >= 0; i--) {
if (i >= tempStart && i <= tempEnd) {
tempPath[i] = adjust;
} else {
tempPath[i] = listOfPaths.get(listIndex)[i];
}
}
System.out.println(Arrays.toString(tempPath));
setOfPaths.add(tempPath);
listOfPaths.add(tempPath);
maximumPermutations = setOfPaths.size();
findAltRoute(listIndex, returnSearchInt);
}
private static void returnAltRoute(int listIndex, int tempStart, int tempEnd, byte adjust) {
byte[] tempPath = new byte[listOfPaths.get(listIndex).length];
for (int i = maximumSteps - 1; i >= 0; i--) {
if (i >= tempStart && i <= tempEnd) {
tempPath[i] = adjust;
} else {
tempPath[i] = listOfPaths.get(listIndex)[i];
}
}
System.out.println(Arrays.toString(tempPath));
setOfPaths.add(tempPath);
listOfPaths.add(tempPath);
maximumPermutations = setOfPaths.size();
}
private static void justFollowThePath() {
for (byte[] bs : setOfPaths) {
//System.out.println(tempSrc.toString());
for (int i = 0; i < maximumSteps && tempSrc.compareTo(BigInteger.ZERO) > 0; i++) {
if (tempSrc.compareTo(BigInteger.ONE) == 0)
if (i < steps) {
steps = i;
maximumSteps = steps;
//System.out.println(i);
// System.out.println(Arrays.toString(tempPath));
break;
} else
break;
if (bs[i] == 2 && tempSrc.mod(TWO) != BigInteger.ZERO)
break;
tempSrc = transform(tempSrc, bs[i]);
}
tempSrc = src.add(BigInteger.ZERO);
}
}
private static void followThePathsWithAlternateRoutes(byte[] tempPath, int startingSearchIndex) {
if (tempPath[maximumSteps - 1] == -1) {
// followThePathsWithAlternateRoutesWasCalledOnce = true;
recurAlt(tempPath, maximumSteps - 1, maximumSteps - 1, (byte) 1, maximumSteps - 1);
}
for (int i = startingSearchIndex - 1; i >= 0; i--) {
if (tempPath[i] == -1 && tempPath[i + 1] == 2) {
for (int k = i - 1; k > 0; k--) {
if (tempPath[k] == 2) {
recurAlt(tempPath, i, k + 1, (byte) 1, k);
}
}
}
}
System.out.println();
for (int i = maximumSteps - 1, k = 0; i >= 0 && tempSrc.compareTo(BigInteger.ZERO) > 0; i--, k++) {
if (tempSrc.compareTo(BigInteger.ONE) <= 0)
if (k < steps) {
steps = k;
maximumSteps = steps;
System.out.println(Arrays.toString(tempPath));
break;
} else
break;
if (tempPath[i] == 2 && tempSrc.mod(TWO) != BigInteger.ZERO)
break;
tempSrc = transform(tempSrc, tempPath[i]);
}
tempSrc = src.add(BigInteger.ZERO);
}
private static BigInteger transform(BigInteger temp, byte i) {
// TODO Auto-generated method stub
return (i == -1) ? tempSrc.subtract(BigInteger.ONE)
: (i == 1) ? tempSrc.add(BigInteger.ONE) : (i == 2) ? tempSrc.divide(TWO) : null;
}
private static void recurAlt(byte[] tempPath, int tempStart, int tempEnd, byte adjust, int returnSearchInt) {
// TODO Auto-generated method stub
byte[] temp = new byte[tempPath.length];
for (int i = 0; i < temp.length; i++) {
if (i >= tempStart && i <= tempEnd)
temp[i] = adjust;
else
temp[i] = tempPath[i];
}
followThePathsWithAlternateRoutes(temp, returnSearchInt);
}
}
there are other things i've tried, but you can see where i'm going. Any pointers?
If the number is even, divide by 2. If the number is 3 mod 4, add one unless the number is actually 3. Otherwise, subtract one. Repeat until you get to 1.
Here's a proof.
First note that if the number is even, it only makes sense to divide by 2. Because if you perform some number (say 2k) of +1 or -1, then divide by 2 that's the same as dividing by two and then adding or subtracting k 1's. So by dividing by two first, you save operations.
So the only question is whether to add or subtract 1 when the number is odd. You can prove by induction that the given strategy is correct.
An odd number n is either 4x+1 or 4x+3. Note that any sequence of operations (when we're dividing by 2 whenever possible) will at some point reach either x or x+1.
We'll consider each of these in turn, and count shortest paths to x and x+1. (Checking that I've correctly identified shortest paths is omitted).
In the first case (4x+1), by subtracting one first we can get to x in 3 steps (4x+1->4x->2x->x) and x+1 in 4 steps (4x+1->4x->2x->x->x+1). By adding one first, we can get to x in 4 steps (4x+1->4x+2->2x+1->2x->x) and x+1 in 4 steps (4x+1->4x+2->2x+1->2x+2->x+1). So we might as well always subtract 1 first.
In the second case (4x+3), by subtracting one first we can get to x in 4 steps (4x+3->4x+2->2x+1->2x->x), and x+1 in 4 steps (4x+3->4x+2->2x+1->2x+2->x+1). By adding one first, we can get to x+1 in 3 steps (4x+3->4x+4->2x+2->x+1), and x in 4 steps (4x+3->4x+4->2x+2->x+1->x). So we might as well always add 1 first in this case.
In the second case, if x=0 (that is n=4*0+3=3), then our reasoning doesn't quite work, since we won't, in fact, reach x. In this case we should subtract one and divide by 2 to reach 1.
The question is labelled java, but here's some pseudocode (actually Python) to produce optimal chains:
def chain(n):
while n:
print n,
if n % 2 == 0:
n //= 2
elif n % 4 == 3 and n != 3:
n += 1
else:
n -= 1
chain(11)
My first approach will be using BFS.
There are only 2 or 3 state transition ( I assume we can't divide odd number).
But this approach only fast enough for small number.
The second approach will be using A* with smallest number as heuristic function. For example when I start with "12", I can go to:
"11", "13", "6". I will choose "6" for the next state because it closer to "1".
However this approach still not fast enough.
My third approach will be generate the answer from "1" to "100" then looking for the pattern / formula.
Edit: remove wrong formula.
I have given a sequence of N numbers (4 ≤ N ≤ 150). One index i (0 < i < N) is picked and multiplied with the left and the right number, in other words with i-1 and i+1. Then the i-th number is removed. This is done until the sequence has only two numbers left over. The goal is to find the smallest sum of these products which obviously depends on the order in which the indices are picked.
E.g. for the sequence 44, 45, 5, 39, 15, 22, 10 the smallest sum would be 17775
using the indices in the following order: 1->3->4->5->2 which is the sum:
44*45*5 + 5*39*15 + 5*15*22 + 5*22*10 + 44*5*10 = 9900 + 2925 + 1650 + 1100 + 2200 = 17775
I have found a solution using a recursive function:
public static int smallestSum(List<Integer> values) {
if (values.size() == 3)
return values.get(0) * values.get(1) * values.get(2);
else {
int ret = Integer.MAX_VALUE;
for (int i = 1; i < values.size() - 1; i++) {
List<Integer> copy = new ArrayList<Integer>(values);
copy.remove(i);
int val = smallestSum(copy) + values.get(i - 1) * values.get(i) * values.get(i + 1);
if (val < ret) ret = val;
}
return ret;
}
}
However, this solution is only feasible for small N but not for a bigger amount of numbers. What I am looking for is a way to do this using an iterative Dynamic Programming approach.
The optimal substructure needed for a DP is that, given the identity of the last element removed, the elimination strategy for the elements to the left is independent of the elimination strategy for the elements to the right. Here's a new recursive function (smallestSumA, together with the version from the question and a test harness comparing the two) incorporating this observation:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Foo {
public static void main(String[] args) {
Random r = new Random();
for (int i = 0; i < 10000; i++) {
List<Integer> values = new ArrayList<Integer>();
for (int n = 3 + r.nextInt(8); n > 0; n--) {
values.add(r.nextInt(100));
}
int a = smallestSumA(values, 0, values.size() - 1);
int q = smallestSumQ(values);
if (q != a) {
System.err.println("oops");
System.err.println(q);
System.err.println(a);
System.err.println(values);
}
}
}
public static int smallestSumA(List<Integer> values, int first, int last) {
if (first + 2 > last)
return 0;
int ret = Integer.MAX_VALUE;
for (int i = first + 1; i <= last - 1; i++) {
int val = (smallestSumA(values, first, i)
+ values.get(first) * values.get(i) * values.get(last) + smallestSumA(values, i, last));
if (val < ret)
ret = val;
}
return ret;
}
public static int smallestSumQ(List<Integer> values) {
if (values.size() == 3)
return values.get(0) * values.get(1) * values.get(2);
else {
int ret = Integer.MAX_VALUE;
for (int i = 1; i < values.size() - 1; i++) {
List<Integer> copy = new ArrayList<Integer>(values);
copy.remove(i);
int val = smallestSumQ(copy) + values.get(i - 1) * values.get(i) * values.get(i + 1);
if (val < ret)
ret = val;
}
return ret;
}
}
}
Invoke as smallestSum(values, 0, values.size() - 1).
To get the DP, observe that there are only N choose 2 different settings for first and last, and memoize. The running time is O(N^3).
If anyone is interested in a DP solution, based on David Eisenstat's recursive solution, here is an iterative one using DP (for many big numbers it's useful to replace int's with long's):
public static int smallestSum(List<Integer> values) {
int[][] table = new int[values.size()][values.size()];
for (int i = 2; i < values.size(); i++) {
for (int j = 0; j + i < values.size(); j++) {
int ret = Integer.MAX_VALUE;
for (int k = j + 1; k <= j + i - 1; k++) {
int val = table[j][k] + values.get(j) * values.get(k) * values.get(j + i) + table[k][j + i];
if (val < ret) ret = val;
}
table[j][j + i] = ret;
}
}
return table[0][values.size() - 1];
}
I'm a beginner, and I'm trying to write a working travelling salesman problem using dynamic programming approach.
This is the code for my compute function:
public static int compute(int[] unvisitedSet, int dest) {
if (unvisitedSet.length == 1)
return distMtx[dest][unvisitedSet[0]];
int[] newSet = new int[unvisitedSet.length-1];
int distMin = Integer.MAX_VALUE;
for (int i = 0; i < unvisitedSet.length; i++) {
for (int j = 0; j < newSet.length; j++) {
if (j < i) newSet[j] = unvisitedSet[j];
else newSet[j] = unvisitedSet[j+1];
}
int distCur;
if (distMtx[dest][unvisitedSet[i]] != -1) {
distCur = compute(newSet, unvisitedSet[i]) + distMtx[unvisitedSet[i]][dest];
if (distMin > distCur)
distMin = distCur;
}
else {
System.out.println("No path between " + dest + " and " + unvisitedSet[i]);
}
}
return distMin;
}
The code is not giving me the correct answers, and I'm trying to figure out where the error is occurring. I think my error occurs when I add:
distCur = compute(newSet, unvisitedSet[i]) + distMtx[unvisitedSet[i]][dest];
So I've been messing around with that part, moving the addition to the very end right before I return distMin and so on... But I couldn't figure it out.
Although I'm sure it can be inferred from the code, I will state the following facts to clarify.
distMtx stores all the intercity distances, and distances are symmetric, meaning if distance from city A to city B is 3, then the distance from city B to city A is also 3. Also, if two cities don't have any direct paths, the distance value is -1.
Any help would be very much appreciated!
Thanks!
Edit:
The main function reads the intercity distances from a text file. Because I'm assuming the number of cities will always be less than 100, global int variable distMtx is [100][100].
Once the matrix is filled with the necessary information, an array of all the cities are created. The names of the cities are basically numbers. So if I have 4 cities, set[4] = {0, 1, 2, 3}.
In the main function, after distMtx and set is created, first call to compute() is called:
int optRoute = compute(set, 0);
System.out.println(optRoute);
Sample input:
-1 3 2 7
3 -1 10 1
2 10 -1 4
7 1 4 -1
Expected output:
10
Here's a working iterative solution to the TSP with dynamic programming. What would make your life easier is to store the current state as a bitmask instead of in an array. This has the advantage that the state representation is compact and can be cached easily.
I made a video detailing the solution to this problem on Youtube, please enjoy! Code was taken from my github repo
/**
* An implementation of the traveling salesman problem in Java using dynamic
* programming to improve the time complexity from O(n!) to O(n^2 * 2^n).
*
* Time Complexity: O(n^2 * 2^n)
* Space Complexity: O(n * 2^n)
*
**/
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class TspDynamicProgrammingIterative {
private final int N, start;
private final double[][] distance;
private List<Integer> tour = new ArrayList<>();
private double minTourCost = Double.POSITIVE_INFINITY;
private boolean ranSolver = false;
public TspDynamicProgrammingIterative(double[][] distance) {
this(0, distance);
}
public TspDynamicProgrammingIterative(int start, double[][] distance) {
N = distance.length;
if (N <= 2) throw new IllegalStateException("N <= 2 not yet supported.");
if (N != distance[0].length) throw new IllegalStateException("Matrix must be square (n x n)");
if (start < 0 || start >= N) throw new IllegalArgumentException("Invalid start node.");
this.start = start;
this.distance = distance;
}
// Returns the optimal tour for the traveling salesman problem.
public List<Integer> getTour() {
if (!ranSolver) solve();
return tour;
}
// Returns the minimal tour cost.
public double getTourCost() {
if (!ranSolver) solve();
return minTourCost;
}
// Solves the traveling salesman problem and caches solution.
public void solve() {
if (ranSolver) return;
final int END_STATE = (1 << N) - 1;
Double[][] memo = new Double[N][1 << N];
// Add all outgoing edges from the starting node to memo table.
for (int end = 0; end < N; end++) {
if (end == start) continue;
memo[end][(1 << start) | (1 << end)] = distance[start][end];
}
for (int r = 3; r <= N; r++) {
for (int subset : combinations(r, N)) {
if (notIn(start, subset)) continue;
for (int next = 0; next < N; next++) {
if (next == start || notIn(next, subset)) continue;
int subsetWithoutNext = subset ^ (1 << next);
double minDist = Double.POSITIVE_INFINITY;
for (int end = 0; end < N; end++) {
if (end == start || end == next || notIn(end, subset)) continue;
double newDistance = memo[end][subsetWithoutNext] + distance[end][next];
if (newDistance < minDist) {
minDist = newDistance;
}
}
memo[next][subset] = minDist;
}
}
}
// Connect tour back to starting node and minimize cost.
for (int i = 0; i < N; i++) {
if (i == start) continue;
double tourCost = memo[i][END_STATE] + distance[i][start];
if (tourCost < minTourCost) {
minTourCost = tourCost;
}
}
int lastIndex = start;
int state = END_STATE;
tour.add(start);
// Reconstruct TSP path from memo table.
for (int i = 1; i < N; i++) {
int index = -1;
for (int j = 0; j < N; j++) {
if (j == start || notIn(j, state)) continue;
if (index == -1) index = j;
double prevDist = memo[index][state] + distance[index][lastIndex];
double newDist = memo[j][state] + distance[j][lastIndex];
if (newDist < prevDist) {
index = j;
}
}
tour.add(index);
state = state ^ (1 << index);
lastIndex = index;
}
tour.add(start);
Collections.reverse(tour);
ranSolver = true;
}
private static boolean notIn(int elem, int subset) {
return ((1 << elem) & subset) == 0;
}
// This method generates all bit sets of size n where r bits
// are set to one. The result is returned as a list of integer masks.
public static List<Integer> combinations(int r, int n) {
List<Integer> subsets = new ArrayList<>();
combinations(0, 0, r, n, subsets);
return subsets;
}
// To find all the combinations of size r we need to recurse until we have
// selected r elements (aka r = 0), otherwise if r != 0 then we still need to select
// an element which is found after the position of our last selected element
private static void combinations(int set, int at, int r, int n, List<Integer> subsets) {
// Return early if there are more elements left to select than what is available.
int elementsLeftToPick = n - at;
if (elementsLeftToPick < r) return;
// We selected 'r' elements so we found a valid subset!
if (r == 0) {
subsets.add(set);
} else {
for (int i = at; i < n; i++) {
// Try including this element
set |= 1 << i;
combinations(set, i + 1, r - 1, n, subsets);
// Backtrack and try the instance where we did not include this element
set &= ~(1 << i);
}
}
}
public static void main(String[] args) {
// Create adjacency matrix
int n = 6;
double[][] distanceMatrix = new double[n][n];
for (double[] row : distanceMatrix) java.util.Arrays.fill(row, 10000);
distanceMatrix[5][0] = 10;
distanceMatrix[1][5] = 12;
distanceMatrix[4][1] = 2;
distanceMatrix[2][4] = 4;
distanceMatrix[3][2] = 6;
distanceMatrix[0][3] = 8;
int startNode = 0;
TspDynamicProgrammingIterative solver = new TspDynamicProgrammingIterative(startNode, distanceMatrix);
// Prints: [0, 3, 2, 4, 1, 5, 0]
System.out.println("Tour: " + solver.getTour());
// Print: 42.0
System.out.println("Tour cost: " + solver.getTourCost());
}
}
I know this is pretty old question but it might help somebody in the future.
Here is very well written paper on TSP with dynamic programming approach
https://github.com/evandrix/SPOJ/blob/master/DP_Main112/Solving-Traveling-Salesman-Problem-by-Dynamic-Programming-Approach-in-Java.pdf
I think you have to make some changes in your program.
Here there is an implementation
http://www.sanfoundry.com/java-program-implement-traveling-salesman-problem-using-nearest-neighbour-algorithm/
Pandigital number is a number that contains the digits 1..number length.
For example 123, 4312 and 967412385.
I have solved many Project Euler problems, but the Pandigital problems always exceed the one minute rule.
This is my pandigital function:
private boolean isPandigital(int n){
Set<Character> set= new TreeSet<Character>();
String string = n+"";
for (char c:string.toCharArray()){
if (c=='0') return false;
set.add(c);
}
return set.size()==string.length();
}
Create your own function and test it with this method
int pans=0;
for (int i=123456789;i<=123987654;i++){
if (isPandigital(i)){
pans++;
}
}
Using this loop, you should get 720 pandigital numbers. My average time was 500 millisecond.
I'm using Java, but the question is open to any language.
UPDATE
#andras answer has the best time so far, but #Sani Huttunen answer inspired me to add a new algorithm, which gets almost the same time as #andras.
C#, 17ms, if you really want a check.
class Program
{
static bool IsPandigital(int n)
{
int digits = 0; int count = 0; int tmp;
for (; n > 0; n /= 10, ++count)
{
if ((tmp = digits) == (digits |= 1 << (n - ((n / 10) * 10) - 1)))
return false;
}
return digits == (1 << count) - 1;
}
static void Main()
{
int pans = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 123456789; i <= 123987654; i++)
{
if (IsPandigital(i))
{
pans++;
}
}
sw.Stop();
Console.WriteLine("{0}pcs, {1}ms", pans, sw.ElapsedMilliseconds);
Console.ReadKey();
}
}
For a check that is consistent with the Wikipedia definition in base 10:
const int min = 1023456789;
const int expected = 1023;
static bool IsPandigital(int n)
{
if (n >= min)
{
int digits = 0;
for (; n > 0; n /= 10)
{
digits |= 1 << (n - ((n / 10) * 10));
}
return digits == expected;
}
return false;
}
To enumerate numbers in the range you have given, generating permutations would suffice.
The following is not an answer to your question in the strict sense, since it does not implement a check. It uses a generic permutation implementation not optimized for this special case - it still generates the required 720 permutations in 13ms (line breaks might be messed up):
static partial class Permutation
{
/// <summary>
/// Generates permutations.
/// </summary>
/// <typeparam name="T">Type of items to permute.</typeparam>
/// <param name="items">Array of items. Will not be modified.</param>
/// <param name="comparer">Optional comparer to use.
/// If a <paramref name="comparer"/> is supplied,
/// permutations will be ordered according to the
/// <paramref name="comparer"/>
/// </param>
/// <returns>Permutations of input items.</returns>
public static IEnumerable<IEnumerable<T>> Permute<T>(T[] items, IComparer<T> comparer)
{
int length = items.Length;
IntPair[] transform = new IntPair[length];
if (comparer == null)
{
//No comparer. Start with an identity transform.
for (int i = 0; i < length; i++)
{
transform[i] = new IntPair(i, i);
};
}
else
{
//Figure out where we are in the sequence of all permutations
int[] initialorder = new int[length];
for (int i = 0; i < length; i++)
{
initialorder[i] = i;
}
Array.Sort(initialorder, delegate(int x, int y)
{
return comparer.Compare(items[x], items[y]);
});
for (int i = 0; i < length; i++)
{
transform[i] = new IntPair(initialorder[i], i);
}
//Handle duplicates
for (int i = 1; i < length; i++)
{
if (comparer.Compare(
items[transform[i - 1].Second],
items[transform[i].Second]) == 0)
{
transform[i].First = transform[i - 1].First;
}
}
}
yield return ApplyTransform(items, transform);
while (true)
{
//Ref: E. W. Dijkstra, A Discipline of Programming, Prentice-Hall, 1997
//Find the largest partition from the back that is in decreasing (non-icreasing) order
int decreasingpart = length - 2;
for (;decreasingpart >= 0 &&
transform[decreasingpart].First >= transform[decreasingpart + 1].First;
--decreasingpart) ;
//The whole sequence is in decreasing order, finished
if (decreasingpart < 0) yield break;
//Find the smallest element in the decreasing partition that is
//greater than (or equal to) the item in front of the decreasing partition
int greater = length - 1;
for (;greater > decreasingpart &&
transform[decreasingpart].First >= transform[greater].First;
greater--) ;
//Swap the two
Swap(ref transform[decreasingpart], ref transform[greater]);
//Reverse the decreasing partition
Array.Reverse(transform, decreasingpart + 1, length - decreasingpart - 1);
yield return ApplyTransform(items, transform);
}
}
#region Overloads
public static IEnumerable<IEnumerable<T>> Permute<T>(T[] items)
{
return Permute(items, null);
}
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> items, IComparer<T> comparer)
{
List<T> list = new List<T>(items);
return Permute(list.ToArray(), comparer);
}
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> items)
{
return Permute(items, null);
}
#endregion Overloads
#region Utility
public static IEnumerable<T> ApplyTransform<T>(
T[] items,
IntPair[] transform)
{
for (int i = 0; i < transform.Length; i++)
{
yield return items[transform[i].Second];
}
}
public static void Swap<T>(ref T x, ref T y)
{
T tmp = x;
x = y;
y = tmp;
}
public struct IntPair
{
public IntPair(int first, int second)
{
this.First = first;
this.Second = second;
}
public int First;
public int Second;
}
#endregion
}
class Program
{
static void Main()
{
int pans = 0;
int[] digits = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (var p in Permutation.Permute(digits))
{
pans++;
if (pans == 720) break;
}
sw.Stop();
Console.WriteLine("{0}pcs, {1}ms", pans, sw.ElapsedMilliseconds);
Console.ReadKey();
}
}
This is my solution:
static char[][] pandigits = new char[][]{
"1".toCharArray(),
"12".toCharArray(),
"123".toCharArray(),
"1234".toCharArray(),
"12345".toCharArray(),
"123456".toCharArray(),
"1234567".toCharArray(),
"12345678".toCharArray(),
"123456789".toCharArray(),
};
private static boolean isPandigital(int i)
{
char[] c = String.valueOf(i).toCharArray();
Arrays.sort(c);
return Arrays.equals(c, pandigits[c.length-1]);
}
Runs the loop in 0.3 seconds on my (rather slow) system.
Two things you can improve:
You don't need to use a set: you can use a boolean array with 10 elements
Instead of converting to a string, use division and the modulo operation (%) to extract the digits.
Using a bit vector to keep track of which digits have been found appears to be the fastest raw method. There are two ways to improve it:
Check if the number is divisible by 9. This is a necessary condition for being pandigital, so we can exclude 88% of numbers up front.
Use multiplication and shifts instead of divisions, in case your compiler doesn't do that for you.
This gives the following, which runs the test benchmark in about 3ms on my machine. It correctly identifies the 362880 9-digit pan-digital numbers between 100000000 and 999999999.
bool IsPandigital(int n)
{
if (n != 9 * (int)((0x1c71c71dL * n) >> 32))
return false;
int flags = 0;
while (n > 0) {
int q = (int)((0x1999999aL * n) >> 32);
flags |= 1 << (n - q * 10);
n = q;
}
return flags == 0x3fe;
}
My solution involves Sums and Products.
This is in C# and runs in about 180ms on my laptop:
static int[] sums = new int[] {1, 3, 6, 10, 15, 21, 28, 36, 45};
static int[] products = new int[] {1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
static void Main(string[] args)
{
var pans = 0;
for (var i = 123456789; i <= 123987654; i++)
{
var num = i.ToString();
if (Sum(num) == sums[num.Length - 1] && Product(num) == products[num.Length - 1])
pans++;
}
Console.WriteLine(pans);
}
protected static int Sum(string num)
{
int sum = 0;
foreach (char c in num)
sum += (int) (c - '0');
return sum;
}
protected static int Product(string num)
{
int prod = 1;
foreach (char c in num)
prod *= (int)(c - '0');
return prod;
}
Why find when you can make them?
from itertools import *
def generate_pandigital(length):
return (''.join for each in list(permutations('123456789',length)))
def test():
for i in range(10):
print i
generate_pandigital(i)
if __name__=='__main__':
test()
J does this nicely:
isPandigital =: 3 : 0
*./ (' ' -.~ ": 1 + i. # s) e. s =. ": y
)
isPandigital"0 (123456789 + i. 1 + 123987654 - 123456789)
But slowly. I will revise. For now, clocking at 4.8 seconds.
EDIT:
If it's just between the two set numbers, 123456789 and 123987654, then this expression:
*./"1 (1+i.9) e."1 (9#10) #: (123456789 + i. 1 + 123987654 - 123456789)
Runs in 0.23 seconds. It's about as fast, brute-force style, as it gets in J.
TheMachineCharmer is right. At least for some the problems, it's better to iterate over all the pandigitals, checking each one to see if it fits the criteria of the problem. However, I think their code is not quite right.
I'm not sure which is better SO etiquette in this case: Posting a new answer or editing theirs. In any case, here is the modified Python code which I believe to be correct, although it doesn't generate 0-to-n pandigitals.
from itertools import *
def generate_pandigital(length):
'Generate all 1-to-length pandigitals'
return (''.join(each) for each in list(permutations('123456789'[:length])))
def test():
for i in range(10):
print 'Generating all %d-digit pandigitals' % i
for (n,p) in enumerate(generate_pandigital(i)):
print n,p
if __name__=='__main__':
test()
You could add:
if (set.add(c)==false) return false;
This would short circuit a lot of your computations, since it'll return false as soon as a duplicate was found, since add() returns false in this case.
bool IsPandigital (unsigned long n) {
if (n <= 987654321) {
hash_map<int, int> m;
unsigned long count = (unsigned long)(log((double)n)/log(10.0))+1;
while (n) {
++m[n%10];
n /= 10;
}
while (m[count]==1 && --count);
return !count;
}
return false;
}
bool IsPandigital2 (unsigned long d) {
// Avoid integer overflow below if this function is passed a very long number
if (d <= 987654321) {
unsigned long sum = 0;
unsigned long prod = 1;
unsigned long n = d;
unsigned long max = (log((double)n)/log(10.0))+1;
unsigned long max_sum = max*(max+1)/2;
unsigned long max_prod = 1;
while (n) {
sum += n % 10;
prod *= (n%10);
max_prod *= max;
--max;
n /= 10;
}
return (sum == max_sum) && (prod == max_prod);
}
I have a solution for generating Pandigital numbers using StringBuffers in Java. On my laptop, my code takes a total of 5ms to run. Of this only 1ms is required for generating the permutations using StringBuffers; the remaining 4ms are required for converting this StringBuffer to an int[].
#medopal: Can you check the time this code takes on your system?
public class GenPandigits
{
/**
* The prefix that must be appended to every number, like 123.
*/
int prefix;
/**
* Length in characters of the prefix.
*/
int plen;
/**
* The digit from which to start the permutations
*/
String beg;
/**
* The length of the required Pandigital numbers.
*/
int len;
/**
* #param prefix If there is no prefix then this must be null
* #param beg If there is no prefix then this must be "1"
* #param len Length of the required numbers (excluding the prefix)
*/
public GenPandigits(String prefix, String beg, int len)
{
if (prefix == null)
{
this.prefix = 0;
this.plen = 0;
}
else
{
this.prefix = Integer.parseInt(prefix);
this.plen = prefix.length();
}
this.beg = beg;
this.len = len;
}
public StringBuffer genPermsBet()
{
StringBuffer b = new StringBuffer(beg);
for(int k=2;k<=len;k++)
{
StringBuffer rs = new StringBuffer();
int l = b.length();
int s = l/(k-1);
String is = String.valueOf(k+plen);
for(int j=0;j<k;j++)
{
rs.append(b);
for(int i=0;i<s;i++)
{
rs.insert((l+s)*j+i*k+j, is);
}
}
b = rs;
}
return b;
}
public int[] getPandigits(String buffer)
{
int[] pd = new int[buffer.length()/len];
int c= prefix;
for(int i=0;i<len;i++)
c =c *10;
for(int i=0;i<pd.length;i++)
pd[i] = Integer.parseInt(buffer.substring(i*len, (i+1)*len))+c;
return pd;
}
public static void main(String[] args)
{
GenPandigits gp = new GenPandigits("123", "4", 6);
//GenPandigits gp = new GenPandigits(null, "1", 6);
long beg = System.currentTimeMillis();
StringBuffer pansstr = gp.genPermsBet();
long end = System.currentTimeMillis();
System.out.println("Time = " + (end - beg));
int pd[] = gp.getPandigits(pansstr.toString());
long end1 = System.currentTimeMillis();
System.out.println("Time = " + (end1 - end));
}
}
This code can also be used for generating all Pandigital numbers(excluding zero). Just change the object creation call to
GenPandigits gp = new GenPandigits(null, "1", 9);
This means that there is no prefix, and the permutations must start from "1" and continue till the length of the numbers is 9.
Following are the time measurements for different lengths.
#andras: Can you try and run your code to generate the nine digit Pandigital numbers? What time does it take?
This c# implementation is about 8% faster than #andras over the range 123456789 to 123987654 but it is really difficult to see on my test box as his runs in 14ms and this one runs in 13ms.
static bool IsPandigital(int n)
{
int count = 0;
int digits = 0;
int digit;
int bit;
do
{
digit = n % 10;
if (digit == 0)
{
return false;
}
bit = 1 << digit;
if (digits == (digits |= bit))
{
return false;
}
count++;
n /= 10;
} while (n > 0);
return (1<<count)-1 == digits>>1;
}
If we average the results of 100 runs we can get a decimal point.
public void Test()
{
int pans = 0;
var sw = new Stopwatch();
sw.Start();
for (int count = 0; count < 100; count++)
{
pans = 0;
for (int i = 123456789; i <= 123987654; i++)
{
if (IsPandigital(i))
{
pans++;
}
}
}
sw.Stop();
Console.WriteLine("{0}pcs, {1}ms", pans, sw.ElapsedMilliseconds / 100m);
}
#andras implementation averages 14.4ms and this implementation averages 13.2ms
EDIT:
It seems that mod (%) is expensive in c#. If we replace the use of the mod operator with a hand coded version then this implementation averages 11ms over 100 runs.
private static bool IsPandigital(int n)
{
int count = 0;
int digits = 0;
int digit;
int bit;
do
{
digit = n - ((n / 10) * 10);
if (digit == 0)
{
return false;
}
bit = 1 << digit;
if (digits == (digits |= bit))
{
return false;
}
count++;
n /= 10;
} while (n > 0);
return (1 << count) - 1 == digits >> 1;
}
EDIT: Integrated n/=10 into the digit calculation for a small speed improvement.
private static bool IsPandigital(int n)
{
int count = 0;
int digits = 0;
int digit;
int bit;
do
{
digit = n - ((n /= 10) * 10);
if (digit == 0)
{
return false;
}
bit = 1 << digit;
if (digits == (digits |= bit))
{
return false;
}
count++;
} while (n > 0);
return (1 << count) - 1 == digits >> 1;
}
#include <cstdio>
#include <ctime>
bool isPandigital(long num)
{
int arr [] = {1,2,3,4,5,6,7,8,9}, G, count = 9;
do
{
G = num%10;
if (arr[G-1])
--count;
arr[G-1] = 0;
} while (num/=10);
return (!count);
}
int main()
{
clock_t start(clock());
int pans=0;
for (int i = 123456789;i <= 123987654; ++i)
{
if (isPandigital(i))
++pans;
}
double end((double)(clock() - start));
printf("\n\tFound %d Pandigital numbers in %lf seconds\n\n", pans, end/CLOCKS_PER_SEC);
return 0;
}
Simple implementation. Brute-forced and computes in about 140 ms
In Java
You can always just generate them, and convert the Strings to Integers, which is faster for larger numbers
public static List<String> permutation(String str) {
List<String> permutations = new LinkedList<String>();
permutation("", str, permutations);
return permutations;
}
private static void permutation(String prefix, String str, List<String> permutations) {
int n = str.length();
if (n == 0) {
permutations.add(prefix);
} else {
for (int i = 0; i < n; i++) {
permutation(prefix + str.charAt(i),
str.substring(0, i) + str.substring(i + 1, n), permutations);
}
}
}
The below code works for testing a numbers pandigitality.
For your test mine ran in around ~50ms
1-9 PanDigital
public static boolean is1To9PanDigit(int i) {
if (i < 1e8) {
return false;
}
BitSet set = new BitSet();
while (i > 0) {
int mod = i % 10;
if (mod == 0 || set.get(mod)) {
return false;
}
set.set(mod);
i /= 10;
}
return true;
}
or more general, 1 to N,
public static boolean is1ToNPanDigit(int i, int n) {
BitSet set = new BitSet();
while (i > 0) {
int mod = i % 10;
if (mod == 0 || mod > n || set.get(mod)) {
return false;
}
set.set(mod);
i /= 10;
}
return set.cardinality() == n;
}
And just for fun, 0 to 9, zero requires extra logic due to a leading zero
public static boolean is0To9PanDigit(long i) {
if (i < 1e6) {
return false;
}
BitSet set = new BitSet();
if (i <= 123456789) { // count for leading zero
set.set(0);
}
while (i > 0) {
int mod = (int) (i % 10);
if (set.get(mod)) {
return false;
}
set.set(mod);
i /= 10;
}
return true;
}
Also for setting iteration bounds:
public static int maxPanDigit(int n) {
StringBuffer sb = new StringBuffer();
for(int i = n; i > 0; i--) {
sb.append(i);
}
return Integer.parseInt(sb.toString());
}
public static int minPanDigit(int n) {
StringBuffer sb = new StringBuffer();
for(int i = 1; i <= n; i++) {
sb.append(i);
}
return Integer.parseInt(sb.toString());
}
You could easily use this code to generate a generic MtoNPanDigital number checker
I decided to use something like this:
def is_pandigital(n, zero_full=True, base=10):
"""Returns True or False if the number n is pandigital.
This function returns True for formal pandigital numbers as well as
n-pandigital
"""
r, l = 0, 0
while n:
l, r, n = l + 1, r + n % base, n / base
t = xrange(zero_full ^ 1, l + (zero_full ^ 1))
return r == sum(t) and l == len(t)
Straight forward way
boolean isPandigital(int num,int length){
for(int i=1;i<=length;i++){
if(!(num+"").contains(i+""))
return false;
}
return true;
}
OR if you are sure that the number is of the right length already
static boolean isPandigital(int num){
for(int i=1;i<=(num+"").length();i++){
if(!(num+"").contains(i+""))
return false;
}
return true;
}
I refactored Andras' answer for Swift:
extension Int {
func isPandigital() -> Bool {
let requiredBitmask = 0b1111111111;
let minimumPandigitalNumber = 1023456789;
if self >= minimumPandigitalNumber {
var resultBitmask = 0b0;
var digits = self;
while digits != 0 {
let lastDigit = digits % 10;
let binaryCodedDigit = 1 << lastDigit;
resultBitmask |= binaryCodedDigit;
// remove last digit
digits /= 10;
}
return resultBitmask == requiredBitmask;
}
return false;
}
}
1023456789.isPandigital(); // true
great answers, my 2 cents
bool IsPandigital(long long number, int n){
int arr[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, amax = 0, amin;
while (number > 0){
int rem = number % 10;
arr[rem]--;
if (arr[rem] < 0)
return false;
number = number / 10;
}
for (int i = 0; i < n; i++){
if (i == 0)
amin = arr[i];
if (arr[i] > amax)
amax = arr[i];
if (arr[i] < amin)
amin = arr[i];
}
if (amax == 0 && amin == 0)
return true;
else
return false;
}