Առաջին
գրառման մեջ ես ներկայացրեցի դրական ամբողջ թվի ֆակտորիալը հաշվելու ծրագիրը որպես Java ծրագրավորման լեզվով գրված առաջին ծրագիր։ Այն աշխատում է և արտածում է 12 թվի ֆակտորիալ։ Այն ցույց է տալիս, թե ինչպես սահմանել
Factorial
դասը և նրա համար սահմանել
main
ստատիկ մեթոդը, հայտարարել ամբողջաթիվ (
int
) փոփոխականներ ու նրանց վերագրել արժեքներ, կազմակերպել կրկնություն (
while
) և արտածել հաշվարկման արդյունքները (
System.out.println()
)։ Այդ ծրագրի օրինակով ցուցադրվեց նաև թե ինչպես թարգմանել Java լեզվով գրված կոդն ու կատարել վիրտուալ մեքենայի օգնությամբ։
Բայց խնդրի լուծման տեսակետից բերված ծրագիրն ուներ մի քանի թերություններ։ Թվարկեմ դրանք և փորձեմ առաջարկել լուծումներ։
int
տիպը Java լեզվում ուն 32 բիթ երկարություն և, քանի որ ֆակտորիալը արագ աճող ֆունկցիա է, նրանում կարելի է հաշվել 1-ից 12 թվերի ֆակտորիալները։ Լուծում կարող էր հանդիսանալ 64 բիթ երկարությամբ long
տիպը, բայց նրանով էլ կարելի է հաշվել միայն 1-ից 20 թվերի ֆակտորիալները։
- Ծրագիրը գրված է միայն 12 թվի ֆակտորիալը հաշվելու համար։ Այն հնարավորություն չունի օգտագործողից հարցնելու թիվը նրա ֆակտորիալը հաշվելու համար։
- Ֆակտորիալի հաշվարկը կատարվում է
main
մեթոդի մարմնում։ Ավելի հարմար կլիներ սահմանել մի ֆունկցիա, որն արգումենտում ստանում է դրական ամբողջ թիվ և վերադարձնում է նրա ֆակտորիալը։
* * *
Թվի երկարության հետ կապված հարցերը լուծելու լավագույն միջոցը նախատեսված է Java լեզվի ստանդարտ գրադարանի
java.math
փաթեթում։ Այդ փաթեթի
BigInteger
դասով իրականացված են անսահմանափակ երկարությամբ ամբողջ թվերը։ Այդ դասը մեր ծրագրում մատչելի դարձնելու համար
Factorial
դասի սահմանումից առաջ պետք է գրել.
import java.math.BigInteger;
Սա ցույց է տալիս, որ ծրագրում կարող ենք հայտարարել և օգտագործել
BigInteger
դասի նմուշներ։
BigInteger
դասն ունի մի քանի կոնստրուկտորներ, որոնք ստեղծում և արժեքավորում են օբյեկտը, բայց դրանցից մեզ հետաքրքրելու է միայն
BigInteger(String val)
տարբերակը, որը օբյեկտն արժեքավորում է թվի տեքստային ներկայացմամբ։ Օրինակ,
123456789012345678901234567890 արժեքով
BigInteger
օբյեկտ ստեղծելու համար պետք է գրել.
BigInteger num1 = new BigInteger("123456789012345678901234567890");
Բացի Java լեզվի բազային տիպերով ներկայացվող օբյեկտներից, իսկ դրանք են
byte
(մեկ բայթ),
short
(կարճ ամբողջ թիվ, 16 բիթ),
int
(ամբողջ թիվ, 32 բիթ),
long
(երկար ամբողջ թիվ, 64 բիթ),
float
(սահող կետով թիվ, 32 բիթ),
double
(կրկնակի ճշտության իրական թիվ, 64 բիթ),
boolean
(տրամաբանական) և
char
(նիշ), բոլոր մյուս օբյեկտները կառուցվում են
new
գործողության օգտագործմամբ։
BigInteger num1;
արտահայտությամբ պարզապես հայտարարվում է հղում
BigInteger
տիպի օբյեկտին, իսկ օբյեկտը կառուցվում է
new BigInteger("1234...");
արտահայտությամբ։
Ֆակտորիալի հաշվարկման համար պետք է հայտարարել երկու
BigInteger
օբյեկտ.
prod
, որը պետք է ունենա
1 նախնական արժեքը, և
n
, որի ֆակտորիալը պետք է հաշվել։ Այս անգամ
n
-ի սկսզբնական արժեք ընդունենք
200 թիվը.
BigInteger prod = BigInteger.ONE;
BigInteger n = new BigInteger("200");
BigInteger
դասում հայտարարված է
1 թվային արժեքով
ONE
հաստատունը, որն օգտագործված է որպես
prod
փոփոխականի նախնական արժեք։ Նույն դասում սահմանված են նաև
ZERO
և
TEN
հաստատունները՝ համապատասխանաբար
0 և
10 թվային արժեքներով։
Հաշվարկների համար մեզ հարկավոր են բազմապատկման և հանման գործողությունները։
BigInteger
դասի
multiply մեթոդը կատարում է երկու
BigInteger
-ների բազմապատկում, իսկ
subtract մեթոդը
BigInteger
թվից հանում է մի ուիշը։
compareTo մեթոդն էլ համեմատում է երկու
BigInteger
օբյեկտներ և վերադարձնում է
-1,
0,
1, երբ առաջին թիվը համապատասխանաբար
փոքր է,
հավասար է կամ
մեծ է երկրորդից։
Այս ամենը հաշվի առնելով արդեն կարող ենք մեր հին ծրագրի ցիկլը ձևափոխել և գրել ահա այսպես.
while( n.compareTo(BigInteger.ZERO) == 1 ) {
prod = prod.multiply(n);
n = n.subtract(BigInteger.ONE);
}
* * *
Հաջորդ խնդիրը ծրագրի համար տվյալների ներածումն է։ Պետք է կազմակերպել այնպես, որ ծրագիրն օգտագործողից հարցնի մի որևէ ամբողջ թիվ և հաշվարկի ու արտածի դրա ֆակտորիալը։ Java ստանդարտ գրադարանի
System
դասի
in
դաշտի
read
մեթոդը հնարավորություն է տալիս ստեղնաշարից (ստանդարտ ներածման հոսքից) ներմուծել բայթերի զանգված՝
byte[]
։ Օրինակ, գրենք կոդի մի հատված, որը ներածման հոսքից կարդում է բայթերի զանգված, այն վերածում է տողի, ապա արտածում է ստանդարտ արտածման հոսքի վրա։
Նախ հայտարարենք
buffer
անունով բայթերի զանգված, օրինակ, 20 երկարությամբ, որի մեջ պետք է կարդալ ներածված տողը։
byte[] buffer = new byte[20];
Ապա կանչենք
read
մեթոդը և նրա արգումենտում տանք
buffer
զանգվածը։ Կատարվելուց հետո
buffer
-ը կպարունակի ներածման հոսքից տրված տվյալները (ինչպես նաև ներածման ավարտին տրված նոր տողի սիմվոլը)։
System.in.read(buffer);
Այնուհետև
String
դասի կոնստրուկտորով բայթերի զանգվածից կառուցենք տեքստային ներտայացումը։
String str = new String(buffer);
Եվ վերջապես, արտածենք տեքստային ներկայացումը՝ նախապես
trim
մեթոդով նրա աջ ու ձախ կողմերից հեռացնելով բացատանիշերը։
System.out.println(str.trim());
Այսպիսով, ֆակտորիալի հաշվարկման մեր ծրագրում
n
փոփոխականին արժեք վերագրող «
BigInteger n = new BigInteger("200");
» տողը փոխարինենք հետևյալ հատվածով․
byte[] buffer = new byte[32];
System.in.read(buffer);
String str = new String(buffer);
BigInteger n = new BigInteger(str.trim());
Եվ, ի մի բերելով ամբողջ ասվածը, կունենանք ամբողջական ծրագրի հետևյալ տեսքը․
package factorial;
import java.math.BigInteger;
public class Factorial {
public static void main(String[] args)
{
byte[] buffer = new byte[32];
System.in.read(buffer);
String str = new String(buffer);
BigInteger n = new BigInteger(str.trim());
BigInteger res = BigInteger.ONE;
while( n.compareTo(BigInteger.ZERO) > 0 ) {
res = res.multiply(n);
n = n.subtract(BigInteger.ONE);
}
System.out.println(res);
}
}
Երբ փոձենք «
javac -d . Factorial.java
» հրամանով թարգմանել ծրագիրը, ապա Java լեզվի կոմպիլյատորը կարտածի սխալի հաղորդագրություն՝ ահա այս տեսքով․
Factorial.java:9: unreported exception java.io.IOException; must be caught or declared to be thrown
System.in.read(buffer);
^
1 error
Բանն այն է, որ
read
մեթոդը գեներացնում է
IOException
տիպի բացառություն։ Եվ կոմպիլյատորը պահանջում է կա՛մ որսալ ու մաշկել այդ բացառությունը, կա՛մ հայտարարել, որ
main
մեթոդում գեներացվում են բացառություններ։ Առայժմ հետաձգենք բացառությունների մշակումը, բայց
main
մեթոդի վերնագիրը ձևափոխենք այնպես, որ երևա նրանում գեներացված բացառության տիպը․
public static void main(String[] args) throws IOException
{ ... }
Իհարկե, քանի որ
java.io.IOException
դասը սահմանված է
java.io
փաթեթում,
Factorial
դասի սահմանումից առաջ պետք է
ներմուծել այն։
import java.io.IOException;
* * *
Հիմա սահմանենք
Factorial
դասի երկու նոր
ստատիկ մեթոդներ, որոնցից առաջինը օգտագործողից պահանջում է ներածել թիվ և վերադարձնում է այդ թվի
BigInteger
ներկայացումը։ Իսկ երկրորդը արգումենտում ստանում է
BigInteger
թիվ և վերադարձնում է նրա ֆակտորիալը նորից
BigInteger
տեսքով։
Առաջին մեթոդի սահմանումը շատ պարզ է և ունի հետևյալ տեսքը․
private static BigInteger inputNumber() throws IOException
{
byte[] buffer = new byte[32];
System.in.read(buffer);
String str = new String(buffer);
return new BigInteger(str.trim());
}
private
ծառայողական բառով սահմանված մեթոդները (ինչպես նաև դասերը, դաշտերը և այլն) տեսանելի են միայն դասի սահմանման մեջ։ Այս
inputNuber
մեթոդն օգտագործվելու է միայն
main
մեթոդում, և նրա տեսանելիության տիրույթը սահմանափակված է
private
բառով։
Նշեցինք, որ ֆակտորիալը հաշվող ֆունկցիան ստանում է
BigInteger
թիվը և վերադարձնում է նրա ֆակտորիալը՝ ներից
BigInteger
տեսքով։ Սահմանենք
factorial
ստատիկ մեթոդը, բայց այս անգամ կրկնման հրամանի փոխարեն օգտագործենք
ռեկուրսիա։ (
N=0, ապա
N!=1, հակառակ դեպքում՝
N!=N * (N-1)!)։
private static BigInteger factorial(BigInteger num)
{
if( 0 == num.compareTo(BigInteger.ZERO) )
return BigInteger.ONE;
return num.multiply(factorial(num.subtract(BigInteger.ONE)));
}
Քանի որ
BigInteger
օբյեկտները չփոփոխվող են (immutable),
multiply
(բազմապատկում) և
subtract
(հանում) մեթոդները ստեղծում և վերադարձնում են համապատասխան նոր օբյեկտը։
* * *
Մի վերջին անգամ անգամ ցույց տանք ծրագրի ամբողջական տեսքը և փորձենք հաշվել 1000 թվի ֆակտորիալը։
package factorial;
import java.io.IOException;
import java.math.BigInteger;
public class Factorial {
private static BigInteger inputNumber() throws IOException
{
byte[] buffer = new byte[32];
System.in.read(buffer);
String str = new String(buffer);
return new BigInteger(str.trim());
}
private static BigInteger factorial(BigInteger num)
{
if( 0 == num.compareTo(BigInteger.ZERO) )
return BigInteger.ONE;
return num.multiply(factorial(num.subtract(BigInteger.ONE)));
}
public static void main(String[] args) throws IOException
{
BigInteger n = inputNumber();
BigInteger res = factorial(n);
System.out.println(res);
}
}
Թարգմանենք հետևյալ հրամանով (կամ սեղմենք NetBeans միջավայրի Run կոճակը).
$ javac -d . Factorial.java
Ապա կատարենք ծրագիրը Java վիրտուալ մեքենայով, և ներածենք 1000 թիվը.
$ java factorial/Factorial
1000
Ծրագրի աշխատանքի արդյունքում արտածվելու է հետևյալ բավականին մեծ արժեքը, որը կարելի է ստուգել ու համոզվել որ 1000-ի ֆակտորիալն է.
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000