IT/소스코드

[Java] Polynomial Calculator 2/2

zzun 2003. 7. 10. 06:26
반응형
자료구조 / 2002년 2학기 / 문병로 교수님

[PolyManage.java]
/* 다항식의 각 항들을 LinkedList에 넣어 저장하면서, 동시에 그 다항식들의 집합도 LinkedList에 저장하는 방식입니다.
  즉, bigList의 Node 각각은 하나의 다항식(Pn(x))를 뜻하고, smallList의 Node 각각은 다항식에서 하나의 항(3x^2)을 가리킵니다.
  그렇지만, 두 List는 같은 LinkedList 자료구조기 때문에 하나의 LinkedList class를 사용해 구현해서 혼돈이 있을수도 있습니다.(code의 효울성을 위해 반복해서 만들필요는 없다고 생각했습니다.)
  구체적으로 말하자면, bigList의 각 node의 number는 다항식번호(n)이고, data는 작은 LinkedList 하나를 가리키는 레퍼런스라 할 수 있습니다.
  반면, smallList의 number는 각 항의 지수이고, data는 각 항의 계수를 표현하도록 했습니다.(Double형 Object)
  number와 data의 변수형에 관한 것은 LinkedList class를 참조하십시오.
  극단적인 input(예를들어, 다항식이 '0' 이거나, 혹은 'x', '-x', 'x^1000' 와 같은 경우)을 고려해서 짜도록 노력했습니다. */

import java.util.*;

public class PolyManage
{
       private LinkedList bigList;  // 하나의 큰 LinkedList를 가짐.

       public PolyManage()
       {
               bigList = new LinkedList();  // 초기화
       }

       public boolean isExist(int polyNumber)
       {
               return bigList.jumpToNodeByNumber(polyNumber);  // 찾는 다항식 번호가 있는지 검사함.
       }

       public boolean enterPoly(int polyNumber, String polyString)  // 새 다항식을 넣는 메쏘드
       {
               LinkedList newSmallList = new LinkedList();  // 작은 List를 하나 만듬(다항식이 들어갈 리스트).
               
               polyString = removeWhiteSpace(polyString);  // 입력받은 String의 모든 공백을 일단 없앰(메쏘드 정의는 아래쪽에).

               if (!doubleSignCheck(polyString))  // 혹시 부호(+,-)가 중복되어 있는지 검사함.
                       return false;  // 중복되어 있다면 새 다항식 입력 실패라고 return.

               polyString = standardizePoly(polyString + '+');  // 스트링을 표준화(아래쪽에 정의)하고, 처리를 효율적으로 하기 위해 맨끝에 '+'를 하나 넣어줌.

               double coeff = 0;
               int exp = 0;                // 계수와 지수를 초기화
               int index = polyString.indexOf("x^");  // index는 최초로 x^ 문자열이 나오는 부분의 인덱스.
               int indexBuffer;        // 다음 iteration을 위해 임시로 저장하는 인덱스 버퍼.
               try
               {
                       while (index != -1)                // 더이상 x^가 발견되지 않을 때 까지.
                       {
                               if (index==0 || (index==1 && polyString.charAt(0)=='+')) // 스트링이 "x^...."로 시작하거나 "+x^..."로 시작하면 계수 = 1.
                                       coeff = 1;
                               else if (index == 1 && polyString.charAt(0)=='-')  // 스트링이 "-x^..."로 시작하면 계수 = -1
                                       coeff = -1;
                               else
                                       coeff = Double.parseDouble(polyString.substring(0,index));  // 그 외의 경우에는 x^ 앞쪽 substring을 double형으로 변환한 것이 계수.
                                                                                                                                                               // 만약 double형으로 변환하는데 exception이 발생하면 아래쪽에서 catch.                                
                               indexBuffer = index;  // 현재 index를 버퍼에 저장해둠.
                               while (polyString.charAt(index)!='+' && polyString.charAt(index)!='-') index++;  // x^ 뒤에 최초로 부호가 나오는 곳까지 index를 이동.
                               exp = Integer.parseInt(polyString.substring(indexBuffer+2,index));  // x^ 와 부호 사이의 substring을 int로 변환한 것이 지수. 마찬가지로 exception은 catch
                               newSmallList.sortedInsertByNumber(exp, new Double(coeff));  // 작은 list에 첫번째 항을 추가. 지수를 기준으로 정렬이 되도록 삽입함(LinkedList class 참조).
                               polyString = polyString.substring(index);  // 더해진 항 부분은 떼어버림. 즉, 지수 뒷부분부터가 새로운 string이 됨.
                               index = polyString.indexOf("x^");  // 다음 x^가 나오는 index를 찾음.
                       }
               }

               catch (NumberFormatException e)  // 혹시 double이나 int형으로 변환하는데 중간에 문자가 들어가 있거나 하면 exception 발생
               {
                       return false;  // 입력이 실패했다고 return
               }

               removeZeroCoeffTerm(newSmallList);  // 리스트에서 data가 0인 node. 즉, 계수가 0인 항은 필요 없으므로 삭제함(아래쪽에 정의).
               bigList.addANodeToEnd(polyNumber, newSmallList);  // 큰 리스트에 다항식번호를 매겨서 새 다항식을 추가함.
               return true;  // 입력이 성공했다고 return
       }

       private String removeWhiteSpace(String s)  // 스트링의 공백을 없애는 메쏘드.
       {
               StringTokenizer st = new StringTokenizer(s);  // 공백을 기준으로 나뉘는 Tokenizer
               String newString = new String();
               while (st.hasMoreTokens()) newString += st.nextToken();  // 나뉘어진 것들을 다시 더함. 자연스레 공백은 사라짐.
               return newString;
       }

       private String standardizePoly(String s)  // 다항식을 표준화하는 작업.
       {
               boolean caught = true;  // 이 전에 부호(+,-)가 catch 되었는가를 기억하고 있는 변수. 맨 앞에 아무것도 없으면 +가 있었다고 볼 수 있으므로 초기값은 true.
               char curChar;        // String의 현재 문자.

               if (s.charAt(0) == 'x')  // 만약 첫문자가 'x'라면
                       s = "1" + s;                // 앞에 계수 역할을 하는 1을 더해줌("+x..."로 시작하거나 "-x..."로 시작하는것은 enterPoly()에서 처리.

               for (int i=1; i<s.length(); i++)  // 첫번째에 x,+,- 가 나오는 경우는 이미 처리했다고 보고, 두번째 문자부터 검사함.
               {                        
                       curChar = s.charAt(i);
                       if (curChar=='+' || curChar=='-')        // 현재 문자가 부호일 때.
                       {
                               if (caught)  // 이전에 부호가 catch가 되고, 이번에 부호가 catch될때까지 'x'가 한번도 안나왔다면
                               {
                                       s = s.substring(0,i) + "x^0" + s.substring(i);        // 상수항이라고 볼 수 있으므로 뒤에 x^0을 추가.
                                       caught = false;  // 현재 'x'가 발견되었다고 볼 수 있으므로 false로 설정.
                               }
                               else
                                       caught = true;  // 만약 이전 부호와 지금 부호 사이에 'x'가 있었다면 값을 다시 true로 설정.
                       }
                       else if (curChar=='x')  // 현재 문자가 'x'일 때.
                       {
                               caught = false;  // 이 부분은 상수항이 아니므로 false라고 설정해주고
                               if (s.charAt(i+1)=='+' || s.charAt(i+1)=='-')  // 혹시 바로 뒤에 부호가 있으면 x^1 항이므로, 뒤에 ^1을 추가.
                                       s = s.substring(0,i+1) + "^1" + s.substring(i+1);
                       }
               }

               return s;
       }

       private boolean doubleSignCheck(String s)  // 부호가 연달아 나오는 경우를 check
       {
               for (int i=0; i<s.length()-1; i++)
                       if (s.charAt(i)=='+' || s.charAt(i)=='-')
                               if (s.charAt(i+1)=='+' || s.charAt(i+1)=='-')
                                       return false;  // 부호가 연달아 나왔으면 false를 return.
               return true;  // 정상이면 true를 return
       }

       private void removeZeroCoeffTerm(LinkedList l)  // 계수가 0인 항을 지우는 메쏘드.
       {
               l.resetIteration();
               while (l.moreToIterate())
               {
                       if (((Double)l.getDataAtCurrent()).doubleValue() == 0)  // 계수가 0이라면
                               l.deleteCurrentNode();  // 그 항을 지움.
                       else
                               l.goToNext();
               }
       }

       public void erasePoly(int polyNumber)  // 다항식을 지우는 메쏘드.
       {
               bigList.resetIteration();
               if(bigList.jumpToNodeByNumber(polyNumber))  // 다항식 번호를 이용해서 검색, 이동.
                       bigList.deleteCurrentNode();                        // 현재 다항식을 삭제.
               else
                       System.out.println("> No P" + polyNumber + "(x) exists!");  // 검색에 실패했을 경우 메시지 출력.
       }
       
       public void evalPoly(int polyNumber, double evalValue)  // 다항식의 값을 계산하는 메쏘드.
       {
               if (bigList.jumpToNodeByNumber(polyNumber))                // 다항식 번호를 이용해서 검색, 이동.
               {
                       double evalResult = 0;  // 계산한 값을 저장할 변수.
                       LinkedList list = (LinkedList)bigList.getDataAtCurrent();  // 찾은 다항식을 불러옴.
                       list.resetIteration();
                       while(list.moreToIterate())
                       {
                               evalResult += ((Double)list.getDataAtCurrent()).doubleValue() * Math.pow(evalValue, list.getNumberAtCurrent());
                               list.goToNext();  // (계수 * evalValue^지수) 를 계속 더해감.
                       }
               
                       System.out.println("> P" + polyNumber + "(" + evalValue + ") = " + evalResult);  // 결과 출력.
               }
               else
                       System.out.println("> No P" + polyNumber + "(x) exists!");  // 검색에 실패했을 경우 메시지 출력.
       }

       public void printPoly()  // 다항식들을 출력하는 메쏘드.
       {
               if (bigList.isEmpty())  // 다항식이 하나도 없을 경우.
                       System.out.println("> No P(x)'s Exists!");
               else
               {
                       int polyNumber;
                       LinkedList smallList;
                       bigList.resetIteration();
                       while(bigList.moreToIterate())
                       {
                               polyNumber = bigList.getNumberAtCurrent();  // 다항식 번호를 불러옴.
                               System.out.print("> P" + polyNumber + "(x) =");
                               printEachPoly((LinkedList)bigList.getDataAtCurrent());  // 다항식을 불러와서, 각 다항식을 출력하는 메쏘드로 보냄.
                               bigList.goToNext();
                       }
               }
       }

       private void printEachPoly(LinkedList poly)  // 각 다항식을 출력하는 메쏘드.
       {
               poly.resetIteration();
               boolean first = true;  // 첫번째 항인지를 기억하고 있는 변수.
               double coeff;
               int exp;
               if (poly.isEmpty())  // 항이 하나도 없는 다항식일 경우.
                       System.out.println(" 0");  // 0을 출력.
               else
               {
                       while(poly.moreToIterate())
                       {
                               coeff = ((Double)poly.getDataAtCurrent()).doubleValue();  // 계수 읽음.
                               exp = poly.getNumberAtCurrent();  // 지수 읽음.
                       
                               if (coeff > 0)  // 계수가 양인데,
                               {
                                       if (first)  // 첫번째 항이면
                                               System.out.print(" ");  // 앞에 '+'를 출력하지 않음.
                                       else
                                               System.out.print(" + ");  // 첫번째 항이 아니면 '+' 부호를 출력.
                               }
                               else  // 계수가 음일 경우. (계수가 0인 경우는 미리 다 없앴다고 생각함.)
                               {
                                       if (first)  // 첫번째 항이면
                                               System.out.print(" -");  // - 부호 뒤에 한칸 안띄움.
                                       else
                                               System.out.print(" - "); // - 부호 뒤에 한칸 띄움. ㅡㅡ;;;;; 스펙에 그렇게 되어 있길래...
                                       coeff = Math.abs(coeff);  // 출력하는 계수는 절대값을 취함. (부호는 이미 출력했으므로)
                               }

                               if (coeff!=1 || (coeff==1 && exp==0))  // 계수가 1이 아니거나, 계수가 1이더라도 상수항일 경우에만 계수를 출력.
                               {
                                       if (coeff == (int)coeff)
                                               System.out.print((int)coeff);  // 만약 정수면 뒤에 ".0"을 출력하지 않기 위해
                                       else
                                               System.out.print(coeff);
                               }

                               switch (exp)
                               {
                               case 0 :
                                       break;  // 상수항이면 지수를 출력하지 않음.
                               case 1 :
                                       System.out.print("x");  // 지수가 1 이면 "x"만 출력.
                                       break;
                               default :
                                       System.out.print("x^" + exp);  // 그 외의 경우에는 "x^지수"를 출력.
                               }
                               first = false;  // 이젠 더 이상 첫번째 항이 아님.
                               poly.goToNext();
                       }
                       System.out.println();
               }
       }

       public void addPoly(int pn1, int pn2)  // 두 다항식을 더해서 출력하는 메쏘드.
       {
               if (bigList.jumpToNodeByNumber(pn1))  // 첫번째 다항식을 다항식 번호로 검색, 이동.
               {
                       LinkedList poly1 = (LinkedList)bigList.getDataAtCurrent();  // 첫번째 다항식 읽어옴.
                       if (bigList.jumpToNodeByNumber(pn2))  // 두번째 다항식을 다항식 번호로 검색, 이동.
                       {
                               LinkedList poly2 = (LinkedList)bigList.getDataAtCurrent();  // 두번째 다항식 읽어옴.
                               System.out.print("P" + pn1 + "(x) + P" + pn2 + "(x) =");
                               printEachPoly(calculateAdd(poly1, poly2));   // 두 다항식을 더한것을 출력.
                       }
                       else
                               System.out.println("> No P" + pn2 + "(x) exists!");  // 두번째 다항식을 찾지 못했을 경우.
               }
               else
                       System.out.println("> No P" + pn1 + "(x) exists!");  // 첫번째 다항식을 찾지 못했을 경우.
       }

       private LinkedList calculateAdd(LinkedList a, LinkedList b)  // 두 다항식을 더하는 메쏘드.
       {
               a.resetIteration();
               b.resetIteration();
               LinkedList newList = new LinkedList();  // 새 리스트를 하나 만듬.
               while (a.moreToIterate())
               {
                       newList.sortedInsertByNumber(a.getNumberAtCurrent(), a.getDataAtCurrent());
                       a.goToNext();  // 첫번째 다항식의 모든 항을 새 다항식에 넣음.
               }
               while (b.moreToIterate())
               {
                       newList.sortedInsertByNumber(b.getNumberAtCurrent(), b.getDataAtCurrent());
                       b.goToNext();  // 두번째 다항식의 모든 항을 새 다항식에 넣으면서 정렬함(지수가 같으면 게수를 더함, 즉 더하는 효과와 같음).
               }
               removeZeroCoeffTerm(newList);  // 계수가 0인 항을 없앰.
               return newList;                // 새로 만든 리스트를 return.
       }

       public void mulPoly(int pn1, int pn2) // 두 다항식을 곱해서 출력하는 메쏘드. 자세한 주석은 addPoly() 함수와 유사.
       {
               if (bigList.jumpToNodeByNumber(pn1))
               {
                       LinkedList poly1 = (LinkedList)bigList.getDataAtCurrent();
                       if (bigList.jumpToNodeByNumber(pn2))
                       {
                               LinkedList poly2 = (LinkedList)bigList.getDataAtCurrent();                                
                               System.out.print("P" + pn1 + "(x) * P" + pn2 + "(x) =");
                               printEachPoly(calculateMul(poly1, poly2));
                       }
                       else
                               System.out.println("> No P" + pn2 + "(x) exists!");
               }
               else
                       System.out.println("> No P" + pn1 + "(x) exists!");
       }

       private LinkedList calculateMul(LinkedList a, LinkedList b)  // 두 다항식을 곱하는 메쏘드.
       {
               LinkedList newList = new LinkedList();
               a.resetIteration();
               while (a.moreToIterate())
               {
                       b.resetIteration();
                       while (b.moreToIterate())
                       {
                               newList.sortedInsertByNumber(a.getNumberAtCurrent() + b.getNumberAtCurrent(),
                                       new Double(((Double)a.getDataAtCurrent()).doubleValue() * ((Double)b.getDataAtCurrent()).doubleValue()));
                               b.goToNext();  // (a의 항 하나, b의 항 하나)의 모든 쌍에 대해 지수는 서로 더하고 계수는 서로 곱해서 새로운 리스트에 넣음.
                       }
                       a.goToNext();
               }
               removeZeroCoeffTerm(newList);
               return newList;
       }

       public void divPoly(int pn1, int pn2)  // 두 다항식을 나누는 메쏘드.
       {
               if (bigList.jumpToNodeByNumber(pn1))
               {
                       LinkedList poly1 = (LinkedList)bigList.getDataAtCurrent();
                       if (bigList.jumpToNodeByNumber(pn2))
                       {
                               LinkedList poly2 = (LinkedList)bigList.getDataAtCurrent();
                               poly1.resetIteration();
                               poly2.resetIteration();
                               if (poly2.isEmpty() || (poly1.getNumberAtCurrent()<poly2.getNumberAtCurrent()))
                                       System.out.println("> Undefined!");  // 피제수의 차수가 제수의 차수보다 작은 경우.
                               else
                               {
                                       LinkedList q = new LinkedList();  // 몫은 비어 있는 상태.
                                       LinkedList r = poly1.listCopy();  // 나머지는 피제수의 값을 일단 그대로 copy.
                                       r.resetIteration();  // 각 다항식의 최고차 항만을 생각했을때,
                                       while (!r.isEmpty() && r.getNumberAtCurrent() >= poly2.getNumberAtCurrent()) // 나머지가 0이 되거나, 나머지의 차수가 제수의 차수보다 작아질때 까지.
                                       {
                                               q.addANodeToEnd(r.getNumberAtCurrent() - poly2.getNumberAtCurrent(),
                                                       new Double( ((Double)r.getDataAtCurrent()).doubleValue() / ((Double)poly2.getDataAtCurrent()).doubleValue()) );
                                                       // 지수가 (나머지의 지수 - 제수의 지수) 이고, 계수가 (나머지의 계수 / 제수의 계수) 인, 항을 몫에다가 더함. (즉, 3x^2 나누기 x 라면 (지수:2-1=1, 계수:3/1=3)인 3x 항을 몫에 더하는 꼴이 됨.)
                                               r = calculateAdd(scalarTimesList(-1,calculateMul(q, poly2)), poly1); // 새로운 나머지 r은 ( (-1)*(몫 * 제수) + 피제수 ) 즉, (피제수 - 몫*제수)가 됨.
                                               removeZeroCoeffTerm(r);
                                               removeZeroCoeffTerm(q);  // 몫과 나머지의 필요없는 항을 삭제.
                                               r.resetIteration();
                                               poly2.resetIteration();  // 최고 차수를 검사해야 하므로 다시 iteration을 초기화함.
                                       }

                                       System.out.print("> Q" + pn1 + pn2 + "(x) =");
                                       printEachPoly(q);
                                       System.out.print("> R" + pn1 + pn2 + "(x) =");
                                       printEachPoly(r);                // 계산 결과 출력.
                               }
                       }
                       else
                               System.out.println("> No P" + pn2 + "(x) exists!");
               }
               else
                       System.out.println("> No P" + pn1 + "(x) exists!");
       }

       private LinkedList scalarTimesList(double scalar, LinkedList l) // 모든 계수에 일정한 상수를 곱해주는 메쏘드. 즉, 다항식 전체가 scalar배 됨.
       {
               LinkedList newList = new LinkedList();
               l.resetIteration();
               while (l.moreToIterate())
               {
                       newList.addANodeToEnd(l.getNumberAtCurrent(), new Double(scalar * ((Double)l.getDataAtCurrent()).doubleValue()));
                       l.goToNext();
               }
               return newList;
       }
}
반응형