2010년 9월 26일 일요일

주기율표에 나오는 원소기호로 만들 수 있는 가장 긴 단어는?

KAIST의 정재승 교수가 트위터에 던진 질문인데, 인터넷에서 몇몇 분이 이 문제를 풀었다. 그 중, 조승연 씨가 파이썬으로 작성한 코드는 아래와 같다.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re

f = open("/usr/share/dict/words", 'r')
maxlen = 0
maxword = ""
pattern = r"^(Ac|Al|Am|Sb|Ar|As|At|Ba|Bk|Be|Bi|Bh|B|Br|Cd\
|Ca|Cf|C|Ce|Cs|Cl|Cr|Co|Cu|Cm|Ds|Db|Dy|Es|Er|Eu|Fm|F|Fr\
|Gd|Ga|Ge|Au|Hf|Hs|He|Ho|H|In|I|Ir|Fe|Kr|La|Lr|Pb|Li|Lu|Mg\
|Mn|Mt|Md|Hg|Mo|Nd|Ne|Np|Ni|Nb|N|No|Os|O|Pd|P|Pt|Pu|Po\
|K|Pr|Pm|Pa|Ra|Rn|Re|Rh|Rg|Rb|Ru|Rf|Sm|Sc|Sg|Se|Si|Ag\
|Na|Sr|S|Ta|Tc|Te|Tb|Tl|Th|Tm|Sn|Ti|W|Uuh|Uuo|Uup|Uuq\
|Uus|Uut|U|V|Xe|Yb|Y|Zn|Zr|Cn)+$"

for line in f:
   ll = line.strip()
   if re.match(pattern, ll, flags=re.IGNORECASE) and \
len(ll) > maxlen:
      maxlen = len(ll)
      maxword = ll

print maxlen, maxword
이 페이지에 나온 답은 22글자의 nonrepresentationalism이지만, 내가 가진 사전 파일에는 이 단어가 없다. 대신 19글자의 nonrepresentational이 걸린다. 이걸 매스매티카로 해보면?
FindMaxElementWord[dictFile_] :=
 Module[{maxlen = 0, maxword = "", elements, pattern, dict, word},
  elements = Table[ElementData[n, "Abbreviation"], {n, 118}];
  pattern =
   "^(" <> Drop[Flatten[Map[{#, "|"} &, elements]], -1] <> ")+$";
  dict = OpenRead[dictFile];
  word = Read[dict, Word];
  While[word =!= EndOfFile,
   If[StringMatchQ[word, RegularExpression[pattern],
      IgnoreCase -> True] &&
     StringLength[word] > maxlen, {maxlen,
      maxword} = {StringLength[word], word}];
   word = Read[dict, Word]];
  Close[dict];
  Return[{maxlen, maxword}]]
알고리듬은 위의 파이썬 코드와 같다. 다만, 원소기호를 자동으로 생성하도록 바꾸었다. FindMaxElementWord["/usr/share/dict/words"]를 실행해보면, 역시 19글자의 nonrepresentational이 걸린다.

2010년 9월 7일 화요일

상수 모으기

매스매티카를 쓰다 보면, 간단한 수식인데 깔끔하게 정리하기 어려울 때가 있다. 예를 들어, Collect[2/x - 2/y, 2]의 결과가


이렇게 나오면 좋겠지만, 결과는 2/x - 2/y이다. 2로 묶어주려면, 어떻게 해야 할까? 이런 어려움에 봉착했을 땐 우선 풀폼을 확인한다.
FullForm[2/x - 2/y]
 

풀폼에서 알 수 있듯이, 첫 번째 항은 2가 두 번째 항은 -2가 곱해져 있어서 2로 묶지 못한다. 답답하게도 -2에서 2를 빼내지 못하는군. 그럼, -2-1*2로 바꿔주면 되겠다 싶지만, 여기에도 문제가 있다. 매스매티카는 -1*2를 입력하면 바로 -2로 바꿔버리므로, 바꾸지 못하게 해야 한다.
2/x - 2/y
% /. -2 -> HoldForm[-1] 2
% // Collect[#, 2] & // ReleaseHold
이제 됐을까? 결과를 보면 알겠지만, 이번에도 실패다. Collect가 문제인데, Collect는 변수만을 모을 수 있기 때문이다. 그럼, 2는 변수가 아닌데 어떻게 해야 할까? 2HoldForm[2]로 치환해서 변수처럼 만들어주면 된다.
2/x - 2/y
% /. -2 -> HoldForm[-1] 2 /. 2 -> HoldForm[2]
% // Collect[#, HoldForm[2]] & // ReleaseHold
이제야 원하는 결과가 나오는군. 휴~