I have this loop which splits a Boolean LinkedList by 8 bits and return the ASCII value of each byte in a buffer. The function return the string buffer.
This code is extremely slow if the LinkedList's size is big. I try to change the Iterator with a simple looping, but it's still slow.
How can this algorithm be really fast ? Maybe with multi-threading ?
Note: The size of the linkedList is not always divisible by 8.
public String toString(){
String buffer = "";
String output = "";
LinkedList<Boolean> bits = this.bits;
for(Iterator it = this.bits.iterator(); it.hasNext();){
if(buffer.length() >= 8){
output += (char)Integer.parseInt(buffer, 2);
buffer = "";
}
buffer += ((Boolean)it.next() == false) ? "0" : "1";
}
if(buffer != "")
output += (char)Integer.parseInt(buffer, 2);
return output;
}
These suggestions will give you enough performance still keeping the code simple and readable. First test using these changes and if doesn't meet your performance requirements then slowly introduce optimization techniques suggested in other answers
Use BitSet instead of LinkedList<Boolean>
use StringBuilder output; instead of String output;
use StringBuilder buffer; instead of String buffer;
Use Integer.valueOf() instead of Integer.parseInt. valueOf uses cache for values below 128 i think.
Use StringBuilder initialized with expected capacity for output:
StringBuilder out = new StringBuilder(bits.size() / 8 + 1);
Use bitwise operations instead of parseInt(), something like this:
int i = 0;
int c = 0;
for(Boolean b: bits){
if (i > 0 && i % 8 == 0){
out.append((char) c);
c = 0;
}
c = (c << 1) | (b ? 1 : 0);
i++;
}
out.append((char) c); // Not sure about the desired behaviour here
String concatenation is slow especially for large lists (since strings are immutable they have to be copied around which takes some time and each copy requires more space as well). Use a StringBuilder instead of a String to append to. In other words: buffer and output should be StringBuilder instances.
As others suggested - use BitSet. For the rest, I think the method below is pretty efficient:
public String toString() {
char[] bytes = new char[bits.size() / 8 + ((bits.size() % 8 > 0) ? 1 : 0)];
int bitCounter = 0;
int word = 0;
int byteCounter = 0;
for (boolean b : bits) {
word = (word << 1) | (b ? 1 : 0);
if (bitCounter == 7) {
bytes[byteCounter] = (char) word;
++byteCounter;
bitCounter = 0;
word = 0;
} else {
++bitCounter;
} // else
} // foreach
bytes[byteCounter] = (char) word;
return new String(bytes);
} // toString() method
Here is possibly a better alternative that does not use byte counter:
public String toString() {
int size = bits.size() / 8 + ((bits.size() % 8 > 0) ? 1 : 0);
if (size == 0) {
return "";
} // if
char[] bytes = new char[size];
int bitCounter = 0;
int word = 0;
for (boolean b : bits) {
if (bitCounter % 8 == 0
&& bitCounter > 0) {
bytes[(bitCounter - 1) / 8] = (char) word;
word = 0;
} // if
word = (word << 1) | (b ? 1 : 0);
++bitCounter;
} // foreach
bytes[size - 1] = (char) word;
return new String(bytes);
} // toString() method
Try to keep buffer as an int. I mean
buffer = buffer << 1 + (((Boolean)it.next() == false) ? 0 : 1);
instead of
buffer += ((Boolean)it.next() == false) ? "0" : "1";
Also use StringBuilder for output. This is a small change here but always a bit.
Try the following:
StringBuilder b = new StringBuilder();
int ch = 0;
int n = 0;
for (Boolean bit : bits) {
ch <<= 1;
if (bit) {
ch++;
}
if (++n == 8) {
b.append((char)ch);
n = 0;
ch = 0;
}
}
if (n > 0) {
b.append((char)ch);
}
System.out.println(b.toString());
Use StringBuffer or stringBuilder instead of String for your buffer and output vars.
String vars are immutable, so every operations creates a new instance in heap, while StringBuilder and StringBuffer are not.
Related
I am using StringBuilder to change a String input and shift it depending on input. This is for the META coding practice website and I am running into an issue with their two Test cases. One is passing and the other is not.
The expected output is stuvRPQrpq-999.#and the input is abcdZXYzxy-999.# with a shift of 200.
Here is my code
String rotationalCipher(String input, int rotationFactor) {
// Write your code here
int shift = rotationFactor % 26;
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
if (character >= 'a' && character <= 'z') {
character = (char) (character + shift);
if (character > 'z') {
character = (char) (character + 'a' - 'z' - 1);
}
output.append(character);
} else if (character >= 'A' && character <= 'Z') {
character = (char) (character + shift);
if (character > 'Z') {
character = (char) (character + 'A' - 'Z' - 1);
}
output.append(character);
} else if (character >= '0' && character <= '9') {
character = (char) (character + shift);
if (character > '9') {
character = (char) (character + '0' - '9' - 1);
}
output.append(character);
} else {
output.append(character);
}
}
return output.toString();
}
My issue is that I am somehow outputting AAA instead of 999 as far as I can tell from tracing my algo seems solid. I looked through JAVA docs StringBuilder page to see if there was any issue with how I was using it. As far as I can tell it should be good to go.
Could anyone lend me an idea of why my output is the way it is?
Here is the test cases code:
String input_1 = "All-convoYs-9-be:Alert1.";
int rotationFactor_1 = 4;
String expected_1 = "Epp-gsrzsCw-3-fi:Epivx5.";
String output_1 = rotationalCipher(input_1, rotationFactor_1);
check(expected_1, output_1);
String input_2 = "abcdZXYzxy-999.#";
int rotationFactor_2 = 200;
String expected_2 = "stuvRPQrpq-999.#";
String output_2 = rotationalCipher(input_2, rotationFactor_2);
check(expected_2, output_2);
Check your maths
200 % 26 = 18 (shift)
'9' + 18 = 57 + 18 = 75 ('K')
75 + '0' = 75 + 48 = 123 ('{')
123 - '9' = 123 - 57 = 66 ('B')
66 - 1 = 65 ('A')
Now, the problem is, between '9' and 'A' there are 7 other characters, so character = (char) (character + ('0' - '9') - 1); would have to become character = (char) (character + ('0' - '9') - 9); to shift 9 back to 9, but that would screw up you other test case
I don't think ASCII manipulation is the way to go here, as there are characters in-between the digits and the upper and lower cased characters which are going to mess things up as the rotation increases.
In fact, for the digits, you really want to rotate using a factor % 10 instead.
A different approach would be to generate a list of characters and apply a shift to those instead. Now if I was doing this, I'd use List and Collections, but lets assume you can't do that for second, instead, we're going to need to apply a shift to an array, for example...
public String[] rotate(String[] original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String[] positiveRotate(String[] original, int offset) {
String[] results = new String[original.length];
int count = original.length - offset;
System.arraycopy(original, count, results, 0, offset);
System.arraycopy(original, 0, results, offset, count);
return results;
}
public String[] negativeRotate(String[] original, int offset) {
String[] results = new String[original.length];
System.arraycopy(original, offset, results, 0, original.length - offset);
System.arraycopy(original, 0, results, original.length - offset, offset);
return results;
}
Now, this has two different methods, one of a "positive" (or "right" shift) and one for a "negative" (or "left" shift). During testing, I found that the you want to "left shift" the array.
Next, we need what we want to shift over...
private String[] digits = "0123456789".split("");
private String[] characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
I've cheated here, you may need to create the array by long hand, but I can't be bothered typing it out.
Please note
I'm using a String array instead of a char array, not hard to change, but I'm been lazy
You could actually do this on the String directly, using things like contains and split to perform the shifting
And then the rotational cipher might look something like...
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String[] shiftedDigits = rotate(digits, -(rotationFactor % 10));
String[] shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = indexOf(value, digits)) > -1) {
output.append(shiftedDigits[index]);
} else if ((index = indexOf(value, characters)) > -1) {
output.append(shiftCharacters[index]);
} else if ((index = indexOf(value.toUpperCase(), characters)) > -1) {
output.append(shiftCharacters[index].toLowerCase());
} else {
output.append(value);
}
}
return output.toString();
}
protected int indexOf(String value, String[] array) {
for (int index = 0; index < array.length; index++) {
if (array[index].equals(value)) {
return index;
}
}
return -1;
}
Then you could just execute it something like...
System.out.println(" --> All-convoYs-9-be:Alert1.");
System.out.println(" Got " + rotationalCipher("All-convoYs-9-be:Alert1.", 4));
System.out.println("Want Epp-gsrzsCw-3-fi:Epivx5.");
System.out.println("");
System.out.println(" --> abcdZXYzxy-999.#");
System.out.println(" Got " + rotationalCipher("abcdZXYzxy-999.#", 200));
System.out.println("Want stuvRPQrpq-999.#");
Which outputs
--> All-convoYs-9-be:Alert1.
Got Epp-gsrzsCw-3-fi:Epivx5.
Want Epp-gsrzsCw-3-fi:Epivx5.
--> abcdZXYzxy-999.#
Got stuvRPQrpq-999.#
Want stuvRPQrpq-999.#
As I said above, you could just use a String instead of an array, in which case it might look something more like...
private String digits = "0123456789";
private String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String shiftedDigits = rotate(digits, -(rotationFactor % 10));
String shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = digits.indexOf(value)) > -1) {
output.append(shiftedDigits.charAt(index));
} else if ((index = characters.indexOf(value)) > -1) {
output.append(shiftCharacters.charAt(index));
} else if ((index = characters.indexOf(value.toUpperCase())) > -1) {
output.append(Character.toLowerCase(shiftCharacters.charAt(index)));
} else {
output.append(value);
}
}
return output.toString();
}
public String rotate(String original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String positiveRotate(String original, int offset) {
String prefix = original.substring(original.length() - offset);
String sufix = original.substring(0, original.length() - offset);
return prefix + sufix;
}
public String negativeRotate(String original, int offset) {
String prefix = original.substring(offset);
String sufix = original.substring(0, offset);
return prefix + sufix;
}
This question already has answers here:
Reverse a string in Java
(36 answers)
how to reverse an inputted number [duplicate]
(3 answers)
Closed 2 years ago.
What's the fastest way to reverse a Long value?
For example, 9876543210 should return 0123456789.
This is what I have right now:
long n = 0, c = 987654321 * 10; // *10 is to get 9876543210 as long value;
while (c > 0) n = n * 10 + c % 10;
System.out.println(n);
Your program encounters an infinite loop because you never change the value of c. Add c /= 10 at the end of each iteration and it will work, albeit the leading zero will be dropped due to it being a number.
long n = 0, c = 9876543210L;
while (c > 0){
n = n * 10 + c % 10;
c /= 10;
}
System.out.println(n);
If you need to have the leading zero, you should consider using Strings instead.
long c = 9876543210L;
final StringBuilder sb = new StringBuilder();
while (c > 0){
sb.append(c % 10);
c /= 10;
}
System.out.println(sb.toString());
I think this can be fast
long x = 1234567890L;
String reversed = new StringBuilder(Long.toString(x)).reverse().toString();
// reversed = "0987654321"
If You want to convert a reversed value to a long again:
long x = -1234567890000L;
StringBuilder reversed = new StringBuilder(Long.toString(x)).reverse();
System.out.println(reversed); // 0000987654321-
if (reversed.charAt(reversed.length() - 1) == '-') //remove `-` at last position
{
reversed.setLength(reversed.length() - 1);
}
while (reversed.charAt(0) == '0') //remove all `0` at the beginning
{
reversed.replace(0, 1, "");
}
System.out.println(reversed); // 987654321
long newLong = Long.parseLong(reversed.toString());
You can simply convert to string and then revert the String, in particular if you want string output in the end anyway. This should be quite straight forward and it has the leading 0, it might also be faster than doing calculations for each positions (but the cost of conversion in valueOf might cancel that advantage):
long c = 9876543210L;
String cAsString = String.valueOf(c);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < cAsString.length(); i++) {
builder.append(cAsString.substring(cAsString.length() - (i + 1), cAsString.length() - i));
}
System.out.println(builder.toString());
or as a one liner
long c = 9876543210L;
String reverted = new StringBuilder(String.valueOf(c)).reverse().toString();
System.out.println(reverted);
I did a little comparison between the options of the current answers:
public static void main(String[] args) {
Instant start = Instant.now();
for (long i = 0; i < 100_000_000; i++) {
stringbuilderWithDirectCalcs(i);
}
Duration duration = Duration.between(start, Instant.now());
System.out.println("Took " + duration);
}
protected static void stringbuilderWithDirectCalcs(long value) {
final StringBuilder sb = new StringBuilder();
while (value > 0) {
sb.append(value % 10);
value /= 10;
}
// System.out.println(sb.toString());
}
protected static void stringbuilderConvenient(long value) {
String reverted = new StringBuilder(String.valueOf(value)).reverse().toString();
//System.out.println(reverted);
}
protected static void stringbuilderHandCrafted(long value) {
String cAsString = String.valueOf(value);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < cAsString.length(); i++) {
builder.append(cAsString.substring(cAsString.length() - (i + 1), cAsString.length() - i));
}
//System.out.println(builder.toString());
}
I did three runs each. The outcome:
stringbuilderConvenient
Took PT6.988S / Took PT6.8S / Took PT6.68S
stringbuilderWithDirectCalcs:
Took PT6.17S / Took PT6.776S / Took PT6.692S
stringbuilderHandCrafted
Took PT18.205S / Took PT16.035S / Took PT17.025S
So, scanning the String by hand and sticking the StringBuilder together step by step seems out of the question. Obviously Stephen C was right in his comment that the calculations happen anyway when converting to String. But the approach based on Stringbuilder.reverse and handcalculating each position are pretty close (and any difference might be due to minor runtime fluctuations). So, one might choose the StringBuilder.reverse method over calculating each position by hand for readability with about the same performance.
So here is the thing.
I have to write code to show a binary number X's next smallest "code-X number" which is bigger than binary number X.
code-X number is a binary number which have no continuously 1. For example: 1100 is not a code X number because it has 11, and 1001001001 is a code-X number
Here is my code
String a = "11001110101010";
String b = "";
int d = 0;
for(int i = a.length()-1; i>0;i--){
if(a.charAt(i) == '1' && a.charAt(i-1)=='1'){
while(a.charAt(i)=='1'){
b = b + '0';
if(i!=0){i--;}
d++;
}
}
b = b + a.charAt(i);
}
StringBuffer c = new StringBuffer(b);
System.out.println(c.reverse());
I plan on copy the binary string to string b, replace every '1' which next i is '1' into '0' and insert an '1'
like:
1100 ---> 10000
but i have no idea how to do it :)
May you help me some how? Thanks
Try this. This handles arbitrary length bit strings. The algorithm is as follows.
Needed to conditionally modify last two bits to force a change if the number is not a codeEx number. This ensures it will be higher. Thanks to John Mitchell for this observation.
Starting from the left, find the first group of 1's. e.g 0110
If not at the beginning replace it with 100 to get 1000
Otherwise, insert 1 at the beginning.
In all cases, replace everything to the right of the grouping with 0's.
String x = "10000101000000000001000001000000001111000000000000110000000000011011";
System.out.println(x.length());
String result = codeX(x);
System.out.println(x);
System.out.println(result);
public static String codeX(String bitStr) {
StringBuilder sb = new StringBuilder(bitStr);
int i = 0;
int len = sb.length();
// Make adjust to ensure new number is larger than
// original. If the word ends in 00 or 10, then adding one will
// increase the value in all cases. If it ends in 01
// then replacing with 10 will do the same. Once done
// the algorithm takes over to find the next CodeX number.
if (s.equals("01")) {
sb.replace(len - 2, len, "10");
} else {
sb.replace(len- 1, len, "1");
}
while ((i = sb.indexOf("11")) >= 0) {
sb.replace(i, len, "0".repeat(len - i));
if (i != 0) {
sb.replace(i - 1, i + 2, "100");
} else {
sb.insert(i, "1");
}
}
String str = sb.toString();
i = str.indexOf("1");
return i >= 0 ? str.substring(i) : str;
}
Prints
10000101000000000001000001000000001111000000000000110000000000011011
10000101000000000001000001000000010000000000000000000000000000000000
Using raw binary you can use the following.
public static void main(String[] args) {
long l = 0b1000010100000000010000010000000011110000000000110000000000011011L;
System.out.println(
Long.toBinaryString(nextX(l)));
}
public static long nextX(long l) {
long l2 = l >>> 1;
long next = Long.highestOneBit(l & l2);
long cutoff = next << 1;
long mask = ~(cutoff - 1);
return (l & mask) | cutoff;
}
prints
1000010100000000010000010000000010000000000000000000000000000000
EDIT: Based on #WJS correct way to find the smallest value just larger.
This is a slight expansion WJS' 99% correct answer.
There is just one thing missing, the number is not incremented if there are no consecutive 1's in the original X string.
This modification to the main method handles that.
Edit; Added an else {}. Starting from the end of the string, all digits should be inverted until a 0 is found. Then we change it to a 1 and break before passing the resulting string to WJS' codeX function.
(codeX version does not include sb.replace(len-2,len,"11");)
public static void main(String[] args) {
String x = "10100";
StringBuilder sb = new StringBuilder(x);
if (!x.contains("11")) {
for (int i = sb.length()-1; i >= 0; i--) {
if (sb.charAt(i) == '0') {
sb.setCharAt(i, '1');
break;
} else {
sb.setCharAt(i, '0');
}
}
}
String result = codeX(sb.toString());
System.out.println(x);
System.out.println(result);
}
I need to add two binary numbers and return the sum. No base conversions are allowed. I know the long method, using arrays. But is there anything shorter ? And by shorter I mean "having smaller code length". Thanks in advance.
In case I was not explicit enough, here is an example:
Input:
1101
11
Output: 10000
The sum of two (binary) integers a and b can be computed as a+b, because all arithmetic is done in binary.
If your input is in human readable strings rather than binary, you can compute their sum in binary using the standard BigInteger class:
import java.math.BigInteger;
String sum(String a, String b) {
return new BigInteger(a, 2).add(new BigInteger(b, 2)).toString(2);
}
Represent the binary numbers as two strings. Reverse the two strings. Then, you can iterate along both strings simultaneously, adding values to three arrays, two which represent the binary digit being added from the strings and the third to represent the carry digit. Create a fourth array representing the answer (you might have to find the limit for how long the answer can possibly be).
fill the answer array by using standard binary adding:
0 + 0 = 0 in the same position,
1 + 0 = 0 + 1 = 1 in the same position,
1 + 1 = 0 in the same position, and carry a 1 to the next position,
1 + 1 + 1 = 1 in the same position, and carry a 1 to the next position.
Reverse the array and you'll have the answer as a binary number.
Here are a couple options, not using any utility methods provided by Java. These don't account for sign (leading +/-) so they only handle whole numbers.
This first method converts the binary strings to integers, adds the integers, then converts the result back to binary. It uses a method-local inner class Convert to avoid duplicating the binaryToInt() code for each of the parameters.
static String binaryAdd1(String binary1, String binary2) {
class Convert {
int binaryToInt(String binary) {
int result = 0;
for (int i = 0; i < binary.length(); i++) {
char c = binary.charAt(i);
result *= 2;
if (c == '1') {
result++;
} else if (c != '0') {
throw new IllegalArgumentException(binary);
}
}
return result;
}
}
final Convert convert = new Convert();
int int1 = convert.binaryToInt(binary1);
int int2 = convert.binaryToInt(binary2);
String result = "";
int temp = int1 + int2;
do {
result = ((temp & 1) == 1 ? '1' : '0') + result;
temp >>= 1;
} while (temp > 0);
return result;
}
This second method uses binary addition logic, as specified by JHaps in his answer, to directly add together the two parameters. No intermediate conversion to integers here.
static String binaryAdd2(String binary1, String binary2) {
final String validDigits = "01";
String binarySum = "";
// pad the binary strings with one more significant digit for carrying
String bin1 = '0' + binary1;
String bin2 = '0' + binary2;
// add them together starting from least significant digit
int index1 = bin1.length() - 1;
int index2 = bin2.length() - 1;
boolean carry = false;
while (index1 >= 0 || index2 >= 0) {
char char1 = bin1.charAt(index1 >= 0 ? index1 : 0);
char char2 = bin2.charAt(index2 >= 0 ? index2 : 0);
if (validDigits.indexOf(char1) < 0)
throw new NumberFormatException(binary1);
if (validDigits.indexOf(char2) < 0)
throw new NumberFormatException(binary2);
if (char1 == char2) {
binarySum = (carry ? '1' : '0') + binarySum;
carry = char1 == '1';
} else {
binarySum = (carry ? '0' : '1') + binarySum;
}
index1--;
index2--;
}
if (binarySum.length() > 1 && binarySum.charAt(0) == '0') {
binarySum = binarySum.substring(1);
}
String result = binarySum.toString();
return result;
}
I found that a similar question has been asked before here : how does Float.toString() and Integer.toString() works?
But this doesn't speak about how that function internally works. When I opened the internally source code of Integer.toString(), it is not understandable for normal junior java programmer.
Can somebody please explain what happens internally in short description ?
NOTE : This was one of the interview questions that I was asked recently. I had no idea about how to answer such question !
The no arg call of integer.toString() simply calls the static method Integer.toString(int i) (using the integer variables own primitive value), which is implemented as below;
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(0, size, buf);
}
First it checks whether it's value is == the lowest possible integer, and returns that if it is equal. If not, then it checks what size the String needs to be using the stringSize() method of Integer to use as the size of an array of characters.
stringSize() implementation below;
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
Once it has a char[] of the correct size, it then populates that array using the getChars() method, implemented below;
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
Explaining each individual step would take far too long for for a stackoverflow answer. The most pertinent section however (as pointed out in the comments) is the getChars() method which, complicated bit shifting aside, is essentially process of elimination for finding each character. I am afraid I can't go into any greater detail than that without going beyond my own understanding.