Calculating the ISBN Number check digit using Java [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm trying to calculate any ISBN-13 Number's Check Digit, I'm not too worried if the ISBN number is valid or invalid, but what I've been trying to do is get the code to work. There are obviously flaws in my interpretation of the algorithm, suggestions on how to fix it are welcome but the primary problem is receiving user input that is too large for an integer variable but I also want to avoid the decimals of the double value.
I already tried to use the BigDecimal and BigNumber but I simply don't have enough experience to be able to understand them completely. This is the algorithm to find d13 (the Check Digit): 10-(d1 +3d2 +d3 +3d4 +d5 +3d6 +d7 +3d8 +d9 +3d10 +d11 +3d12)%10.
The Code is a mess I know. I've used this website as a reference to what I want to do and I've been using this ISBN number as my practice: 9780132130806.
Again my question is how can I print the final ISBN number without decimals and how can I possibly fix my algorithm? (I'd also really appreciate any tips on a website that helps with teaching JOption as that is the prefered method i use because it looks a bit cleaner to me than using the scanner)
import javax.swing.JOptionPane;
import java.math.BigInteger;
public class ISBN
{
//George Sayegh Calculate check Digit ISBN
public static void main(String[] args)
{
//Define Variables
double ISBN12, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12 = 0, D13;
double A = 100000000000L;
double B = 10000000000L;
double C = 1000000000;
double D = 100000000;
double E = 10000000;
double F = 1000000;
double G = 100000;
double H = 10000;
double I = 1000;
double J = 100;
double K = 10;
double L = 1;
//Get ISBN #
String ISBN12text = JOptionPane.showInputDialog("Please enter the first 12 digits of your ISBN number");
ISBN12 = Double.parseDouble(ISBN12text);
//Calculate D1
D1 = ((ISBN12 - (ISBN12 % A)) / A);
//Calculate D2
D2 = ((ISBN12 - (ISBN12 % B)) / B);
//Calculate D3
D3 = ((ISBN12 - (ISBN12 % C)) / C);
//Calculate D4
D4 = ((ISBN12 - (ISBN12 % D)) / D);
//Calculate D5
D5 = ((ISBN12 - (ISBN12 % E)) / E);
//Calculate D6
D6 = ((ISBN12 - (ISBN12 % F)) / F);
//Calculate D7
D7 = ((ISBN12 - (ISBN12 % G)) / G);
//Calculate D8
D8 = ((ISBN12 - (ISBN12 % H)) / H);
//Calculate D9
D9 = ((ISBN12 - (ISBN12 % I)) / J);
//Calculate D10
D10 = ((ISBN12 - (ISBN12 % K)) / K);
//Calculate D11
D11 = ((ISBN12 - (ISBN12 % L)) / L);
//Get D13
D13 = 10 - (D1 + (3 * D2) + D3 + 3 * D4 + D5 + 3 * D6 + D7 + 3 * D8 + D9 + 3 * 10 + D11 + 3 * D12) % 10;
JOptionPane.showMessageDialog(null, D1 +""+ D2 +""+ D3 +""+ D4 +""+ D5 +""+ D6 +""+ D7 +""+ D8 +""+ D9 +""+ D10 +""+ D11 +""+ D12 +""+ 13);
}
}

Try this extremely simple algorithm (written in Python, but it should be very easy to convert to Java). The comments hopefully explain enough. Should convert to not more than 10 lines (not including blank lines for readability)
#// Valid ISBN-12 string from user (12 numbers, doesn't matter what other characters)
isbn12 = '978-0-306-40615' #// Input it from the user, hardcoded Wikipedia example for testing
coeff = 1 #// Multiple used in calculating sum
sum = 0
for d in isbn12: #// Equivalent to for(char d : isbn12.toCharArray())
#// this next block is just a tryParse or an isDigit check
#// if the character is a digit, parse it to an int
#// else continue to next iteration
try:
#// just reassigning the value because-
#// Hey, this is Python!
d = int(d)
except ValueError:
continue
#// Add to sum after multiplying with coeff
sum += d * coeff
#// If coeff was 1, change to 3 and vice versa
#// Equivalent to coeff == 1 ? 3 : 1 in Java
coeff = 3 if coeff == 1 else 1
#// END FOR LOOP
#// Get positive number to be added to make ISBN divisible by 10
#// The extra % 10 at the end is for changing 10 to 0 without if
check_digit = (10 - sum % 10) % 10
(The extra // at the start of each comment is to make SO code formatting think it is a comment)

Here's a much simpler implementation based on this Wikipedia example. It lacks sanity checks---such as whether the input string is a valid ISBN13---but should be enough to get you going. I hope it helps.
Note that the input is an ISBN-13 string with the check digit removed (e.g. 978013213080 instead of 9780132130806); the program prints the check digit on the output; you should be able modify it if this is not what you want.
public class CheckISBN13 {
/* We assume isbnString is a *valid* ISBN-13 with the check digit removed. */
public static int[] stringToInt(String isbnString) {
int[] isbnInt = new int[12];
int j = 0;
for (int i = 0; i < isbnString.length(); ++i) {
if (Character.isDigit(isbnString.charAt(i))) {
isbnInt[j++] = Character.getNumericValue(isbnString.charAt(i));
}
}
return isbnInt;
}
public static int getCheckDigit(int[] isbnInt) {
int val = 0;
for (int i = 0; i < isbnInt.length; ++i) {
int coeff = (i % 2 == 0 ? 1 : 3);
val += coeff * isbnInt[i];
}
return 10 - (val % 10);
}
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java CheckISBN13 978-0-306-40615");
System.exit(-1);
}
String isbnString = args[0];
int[] isbnInt = stringToInt(isbnString);
System.out.println("Check digit: " + getCheckDigit(isbnInt));
}
}

Related

How to create random value between two different sets of numbers

I am trying to generate a random character between the following two sets of unicode U+0020 to U+007E and U+00A0 to U+00FF. I have created the code to generate a value between U+00A0 to U+00FF but I also need my generator to include the values from U+00A0 to U+00FF, how is this possible? Any help is greatly appreciated, here is what I have so far. (P.S. I am using seed for testing). For example instead of just trying to create a integer between 1-10, I would like to know how to create a random number that could either be between 1-10 or 50-100.
private static char random(){
long seed = 1776;
Random number = new Random(seed);
int randomNumber = number.nextInt(126) + 32;
char a = (char) randomNumber;
return a;
}
To generate a random number to fall in multiple ranges, you first calculate the total number of values in your target dataset, then generate a random number for that total, and assign it to the appropriate set.
Sample code to generate 20 random numbers in ranges 0x20 - 0x7E and 0xA0 - 0xFF, inclusive:
int low1 = 0x20, high1 = 0x7E, low2 = 0xA0, high2 = 0xFF;
int count1 = high1 - low1 + 1;
int count2 = high2 - low2 + 1;
long seed = 1776;
Random rnd = new Random(seed);
for (int i = 0; i < 20; i++) {
int n = rnd.nextInt(count1 + count2);
if (n < count1) { // random number is for range 1
n = n + low1; // offset into range 1
} else { // random number is for range 2
n = n - count1 + low2; // offset into range 2
}
System.out.printf("%02X ", n);
}
Sample Output
6B 6B A5 DA B3 F7 2B C6 AB F2 3F EE F9 A5 28 31 AD D3 66 B0

Java addition giving wrong answer

I am trying to add four integers ie 4+3+2+1 but i get the value 202
class Task3{
public static void main (String args[]){
String x=(args[0]);
int I = Integer.parseInt (x);
char c1 = x.charAt(0);
char c2 = x.charAt(1);
char c3 = x.charAt(2);
char c4 = x.charAt(3);
System.out.println("First and last digit is: " + c1 +"," + c4);
if (c1 > c4)
System.out.println("The first digit is larger");
else
System.out.println("The second digit is larger");
int sum = c1 + c2 + c3 + c4;
System.out.println(sum);
}
}
replace char c1 = x.charAt(0); with Character.getNumericValue(x.charAt(0))
This is because you are adding numeric values of UNICODE code points, not digits represented by the corresponding characters.
In order to get a digit from a character code, call Character.digit(c1, 10) (ten indicates that you want a decimal digit).
int c1 = Character.digit(x.charAt(0), 10);
int c2 = Character.digit(x.charAt(1), 10);
int c3 = Character.digit(x.charAt(2), 10);
int c4 = Character.digit(x.charAt(3), 10);
Change
int sum = c1 + c2 + c3 + c4;
to
int sum = c1 + c2 + c3 + c4 - 4 * '0';
Since c1, c2, c3, c4 are all characters, so a digit say. 4 is taken as '4' that is basically the ASCII value, so to get 4 and not '4' you need to subtract '0' from each of c1, c2, c3, c4, so 4 * '0' subtracted
You are trying to sum up the chars ASCII codes and not the digit values. You have to retrieve the corresponding digit for each character and then evaluate you addition result:
class Task3
{
public static void main (String args[])
{
String x=(args[0]);
int I = Integer.parseInt (x);
char c1 = x.charAt(0);
char c2 = x.charAt(1);
char c3 = x.charAt(2);
char c4 = x.charAt(3);
int i1 = Character.digit(c1, 10);
int i2 = Character.digit(c2, 10);
int i3 = Character.digit(c3, 10);
int i4 = Character.digit(c4, 10);
System.out.println("First and last digit is: " + i1 +"," + i4);
if (i1 > i4)
System.out.println("The first digit is larger");
else
System.out.println("The second digit is larger");
int sum = i1 + i2 + i3 + i4;
System.out.println(sum);
}
}
The reason of the wrong output others already explained it now one of the way to get the correct output
int sum =0;
while(I>0){
int rem = I%10;
sum+=rem;
I = I/10;
}
System.out.println(sum);
I presume you are passing "4321" or similar to the program.
c1 will not be the number 1 but actually the Unicode number representing the character '1' which is actually 31 (in hexadecimal). The latest addition is adding these Unicode numbers.
See http://unicode-table.com/en/ for the list of unicode numbers to characters and also http://docs.oracle.com/javase/tutorial/i18n/text/unicode.html for more details of characters in Java.

Finding the last two digits before the decimal point for the number (4+sqrt(11))^n

I am doing a problem in which I have to find the last two digits before the decimal point for the number [4 + sqrt(11)]n.
For example, when n = 4, [4 + sqrt(11)]4 = 2865.78190... the answer is 65. Where n can vary from 2 <= n <= 109.
My solution - I have tried to build a square root function which calculate the sqrt of 11
which a precision equal to value of n input by the user.
I have used BigDecimal in Java to avoid overflow problems.
public class MathGenius {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
long a = 0;
try {
a = reader.nextInt();
} catch (Exception e) {
System.out.println("Please enter a integer value");
System.exit(0);
}
// Setting precision for square root 0f 11. str contain string like 0.00001
StringBuffer str = new StringBuffer("0.");
for (long i = 1; i <= a; i++)
str.append('0');
str.append('1');
// Calculating square root of 11 having precision equal to number enter
// by the user.
BigDecimal num = new BigDecimal("11"), precision = new BigDecimal(
str.toString()), guess = num.divide(new BigDecimal("2")), change = num
.divide(new BigDecimal("4"));
BigDecimal TWO = new BigDecimal("2.0");
BigDecimal MinusOne = new BigDecimal("-1"), temp = guess
.multiply(guess);
while ((((temp).subtract(num)).compareTo(precision) > 0)
|| num.subtract(temp).compareTo(precision) > 0) {
guess = guess.add(((temp).compareTo(num) > 0) ? change
.multiply(MinusOne) : change);
change = change.divide(TWO);
temp = guess.multiply(guess);
}
// Calculating the (4+sqrt(11))^n
BigDecimal deci = BigDecimal.ONE;
BigDecimal num1 = guess.add(new BigDecimal("4.0"));
for (int i = 1; i <= a; i++)
deci = deci.multiply(num1);
// Calculating two digits before the decimal point
StringBuffer str1 = new StringBuffer(deci.toPlainString());
int index = 0;
while (str1.charAt(index) != '.')
index++;
// Printing output
System.out.print(str1.charAt(index - 2));
System.out.println(str1.charAt(index - 1));
}
}
This solution works up to n = 200, but then it begins to slow down. It stops working for n = 1000.
What is a good method to deal with problem?
2 -- 53
3 -- 91
4 65
5 67
6 13
7 71
8 05
9 87
10 73
11 51
12 45
13 07
14 33
15 31
16 85
17 27
18 93
19 11
20 25
21 47
22 53
23 91
24 65
25 67
At n=22 the results seem to repeat from the position of n=2.
So keep those 20 values in an array in the same order as in your list e.g. nums[20].
Then when the user provides an n:
return nums[(n-2)%20]
There is now a proof of this pattern repeating here.
Alternatively, if you insist on computing at length; since you calculating the power by looping multiplication (and not BigDecimal pow(n)) you could trim the number you are working with at the front to the last 2 digits and the fractional part.
Here is a much simpler solution for you...
Use the rational representation of 4+sqrt(11):
BigInteger hundred = new BigInteger("100");
BigInteger numerator = new BigInteger("5017987099799880733320738241");
BigInteger denominator = new BigInteger("685833597263928519195691392");
BigInteger result = numerator.pow(n).divide(denominator.pow(n)).mod(hundred);
UPDATE:
As you've mentioned in the comments below, this procedure is prone to precision-loss, and will eventually yield an incorrect result. I found this question to be rather interesting on the mathematical aspect, and so I published a question on MO (https://mathoverflow.net/q/158420/27456).
You can read the answer at https://mathoverflow.net/a/158422/27456.

How to use in Java big double numbers?

I've found a solution for calculating number of Pi by using BBS algorithm. But I encountered a problem. I'm missing a precision if using a double variable. Is there any suggestion to fix it?
Here is my code:
public class Pi {
public static void main(String[] args) {
int n = 5;
for (int k = 0; k < n; k++) {
int a0 = (int) Math.pow(16, k);
double a1 = (double) 4 / (8 * k + 1);
double a2 = (double) 2 / (8 * k + 4);
double a3 = (double) 1 / (8 * k + 5);
double a4 = (double) 1 / (8 * k + 6);
double a5 = a1 - a2 - a3 - a4;
double a6 = (double) 1 / a0;
double elem = a5 * a6;
System.out.println(new BigDecimal(elem));
}
}
}
If you need the precision of BigDecimal, you need to use it for all calculations. It is not sufficient to convert the result from double to BigDecimal at the end, because the precision is gone by then.
You need to convert all your aX variables to BigDecimal, and replace operators with calls to the corresponding methods of BigDecimal class:
BigDecimal pi = BigDecimal.ZERO;
for (int k = 0; k < n; k++) {
BigDecimal a0 = new BigDecimal(16).pow(k);
BigDecimal a1 = new BigDecimal(4).divide(new BigDecimal(8*k+1), 20, RoundingMode.HALF_UP);
BigDecimal a2 = new BigDecimal(2).divide(new BigDecimal(8*k+4), 20, RoundingMode.HALF_UP);
BigDecimal a3 = new BigDecimal(1).divide(new BigDecimal(8*k+5), 20, RoundingMode.HALF_UP);
BigDecimal a4 = new BigDecimal(1).divide(new BigDecimal(8*k+6), 20, RoundingMode.HALF_UP);
BigDecimal a5 = a1.subtract(a2).subtract(a3).subtract(a4);
BigDecimal a6 = BigDecimal.ONE.divide(a0, 20, RoundingMode.HALF_UP);
pi.add(a5.multiply(a6));
System.out.println(pi);
}
Demo on ideone.
The problem is that you're using doubles during the calculation itself, thus inevitably losing accuracy. Yes, you're using BigDecimal at the end, but only after already destroying data by putting it in doubles.
The solution is to not use doubles at ANY point in the calculation. Use BigDecimal for every step of the way.
To use a metaphor: What you're doing is trying to pour a swimming pool's amount of water into a glass, then pouring the glass into the pool and expecting it to be filled. No, it won't be, because most of the water didn't fit in the glass and just poured onto the ground.

Decimal Conversion error

I am writing a program that will convert octal numbers to decimals. It compiles right and everything but there is something majorily wrong with my conversion code. It seems perfectly logic to me, however somehow when I run the program the conversions are wrong (i.e. 1 is converted to 36) can someone point out what is going wrong?
public static int convert(int octal)
{
int d1=0,d2=0,d3=0,d4=0,d5=0,d6=0,d7=0,d8=0;
if(octal >=9999999){
d8 = (octal-(octal%10000000));}
if(octal >=999999){
d7 = (octal-(octal%1000000));}
if(octal >=99999){
d6 = (octal-(octal%100000));}
if(octal >=9999){
d5 = (octal-(octal%10000));}
if(octal >= 999){
d4 = (octal-(octal%1000));}
if(octal >= 99){
d3 = (octal-(octal%100));}
if(octal >= 9){
d2 = (octal-(octal%10));}
if(octal >= 0){
d1 = (octal-(octal%1));}
octal = (d8 * 8^7) + (d7 * 8^6) + (d6 * 8^5) + (d5 * 8^4) + (d4 * 8^3) + (d3 * 8^2) + (d2 * 8^1) + (d1 * 8^0);
return octal;
}
this is just my convert method, my main method is what collects the
int octal;
This is the problem:
8^7
The ^ operator doesn't do what you think it does. It does binary XOR...
However, I'd say that the whole design is distinctly suspect. An int value isn't "in" octal or any other base - it's just an integer. The number ten is the number ten, whether that's exressed as "10" in decimal, "A" in hex or "12" in octal. If you've got a sequence of characters which you want to parse as octal digits, the input to the method should be a String, not an int.
Since your method accepts an integer (commonly represented in a String representation as decimal), and what you're outputting is also an integer, you've not really turned into "an octal integer", you've changed it into some other integer completely. You're trying to correctly convert your integer into an octal, and then incorrectly interpreting that octal as a decimal.
If wish to convert an integer into it's octal string representation, you could simply use the following method:
public static String convert(int number) {
Integer.toOctalString(number);
}
And, if you truly want to return an int that represents that octal String parsed as if it was decimal, you could simply do this:
public static int convert(int number) {
return Integer.parseInt(Integer.toOctalString(number));
}
If you have repetitive code, you might consider a loop.
public static void main(String... args) {
for (long i = 7, j = 7; i > 0; i = i * 10 + 1, j = j * 8 + 1) {
long c = convert(i);
if (c != j)
throw new AssertionError(i + ": " + c + " != " + j);
System.out.println(i + " = > " + j);
}
}
public static long convert(long octal) {
long ret = 0;
for (long i = 1000000000000000000L, j = 1L << (3 * 18); i > 0; i /= 10, j >>= 3)
ret += j * (octal / i % 10);
return ret;
}
prints
7 = > 7
71 = > 57
711 = > 457
7111 = > 3657
71111 = > 29257
711111 = > 234057
7111111 = > 1872457
71111111 = > 14979657
711111111 = > 119837257
7111111111 = > 958698057
71111111111 = > 7669584457
711111111111 = > 61356675657
7111111111111 = > 490853405257
71111111111111 = > 3926827242057
711111111111111 = > 31414617936457
7111111111111111 = > 251316943491657
71111111111111111 = > 2010535547933257
711111111111111111 = > 16084284383466057
7111111111111111111 = > 128674275067728457
You have an underlying misconception that's going to keep messing you up.
There is no such thing as an "octal number", per se. An int isn't decimal, or roman numerals, or octal - it's just a number.
An "octal number" really means "a string containing the octal representation of a number".

Categories