Home

Advertisement

Программируем для удовольствия - Ну что, приступим? [entries|archive|friends|userinfo]
Программируем для удовольствия

[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Ну что, приступим? [Mar. 13th, 2008|12:55 pm]
Previous Entry Add to Memories Tell a Friend Next Entry
coding4fun_ru
[zhuzh]
Первое задание — разминочное. Я позаимствовал его из одной англоязычной тусовки, ссылаться на которую пока не буду, потому что это будет не разминка, а жульничество. Credit where credit is due будет при подведении итогов.

Итак, задание. Напишите две программы.
  • combinations:
    • читает из стандартного ввода последовательность слов
    • печатает на стандартный вывод все слова, в которых i-й символ взят из i-го слова
    • пример: на входе слова 01 01 01, на выходе — все трехзначные двоичные числа
  • permutations:
    • читает из стандартного ввода единственное слово
    • печатает на стандартный вывод все слова, полученные из входного слова перестановкой символов
    • пример: на входе слово 123, на выходе — 123 132 213 231 312 321
В каждом слове все символы можно полагать различными (и отличными от пробела). Можно считать, что символы — это байты, так что юникод поддерживать не нужно. Слова на входе и на выходе оканчиваются переводом строки, этот символ не является частью слова. Символов в каждом слове и самих слов будет не более 100.

Постарайтесь сделать вашу реализацию корректной, эффективной и элегантной (в этом порядке).

Пишите на любом языке, который вам нравится, если для него есть бесплатный компилятор или интерпретатор для Linux или Windows (если язык очень уж экзотический, не забудьте указать адрес, с которого этот самый компилятор можно скачать).

Если кому-то это кажется слишком простым — пожалуйста, не пренебрегайте, покажите пример подрастающему поколению! Публикуйте решения в виде комментариев к этому посту (не забудьте преобразовать код в HTML, чтобы ЖЖ не съел ваши пробелы и <>&).

Призов победителям не обещаю, но восхищение современников и благодарность потомков почти гарантированы.

Сроку пусть будет неделя, до 20.03.2008 23:59 UTC, чтобы никто никуда не торопился.
linkReply

Comments:
[User Picture]From: [info]dimrub
2008-03-13 11:12 am (UTC)

(Link)

Про combinations не понял: "все слова" - из данных?
From: [info]zhuzh
2008-03-13 11:16 am (UTC)

(Link)

Нет, не из данных, а все возможные слова, у которых i-й символ взят из i-го входного слова.
[User Picture]From: [info]dimrub
2008-03-13 11:14 am (UTC)

(Link)

Да, и как насчет добавить линк в юзеринфо: http://codepad.org/
[User Picture]From: [info]ilya_dogolazky
2008-03-13 01:36 pm (UTC)

(Link)

И описание полезной команды :so $VIMRUNTIME/syntax/2html.vim
[User Picture]From: [info]uzbeche
2008-03-13 11:17 am (UTC)

(Link)

Присоединяюсь к вопросу по поводу №1 - как определять, что слова кончились и пора печатать ?
From: [info]zhuzh
2008-03-13 11:21 am (UTC)

(Link)

EOF
From: [info]zhuzh
2008-03-13 01:55 pm (UTC)

(Link)

поправка в условии, внимание!
[User Picture]From: [info]uzbeche
2008-03-13 01:42 pm (UTC)

Re: типа тоже решение :-)

(Link)

Трудно поспорить :)
[User Picture]From: [info]uzbeche
2008-03-13 01:43 pm (UTC)

Re: типа тоже решение :-)

(Link)

впрочем, на ввод

a
bc
de

получаем

{a}{b,c}{d,e}
[User Picture]From: [info]ilya_dogolazky
2008-03-13 02:17 pm (UTC)

(Link)

#!/bin/bash
sed 's/\(.\)/\\\1,/g' |
sed 's/.$/}\\/g' |
sed 's/^/{/g' |
sed 's/^.\(..\)..$/\1\\/g' |
sed 's!^.$! >/dev/null \\!' |
sed '1s/^/echo /' |
bash |
sed 's/ /\n/g'
From: [info]zhuzh
2008-03-13 03:35 pm (UTC)

(Link)

лихо.
[User Picture]From: [info]gianthare
2008-03-16 02:00 pm (UTC)

Что там происходит вообще?

(Link)

Сверх того, что с помощью sed генерируется какая-то программа для bash? Хотя можно запустить по частям, конечно
[User Picture]From: [info]swizard
2008-03-13 03:54 pm (UTC)

(Link)

Ну, если втупую, то combinations у меня будет примерно такой (схема, компилятор chicken). Работает, похоже, корректно, но наверняка не самая эффективная реализация :)
[User Picture]From: [info]swizard
2008-03-13 04:49 pm (UTC)

(Link)

А, кстати, вторая задача практически эквивалентна первой :) Дифф к предыдущей реализации такой:

-             (permute (read-lists-of-chars)))
+             (permute (let ((string (read-line)))
+                        (make-list (string-length string)
+                                   (string->list string)))))

[User Picture]From: [info]_navi_
2008-03-13 08:08 pm (UTC)

Первая (Haskell)

(Link)

Если немного пренебречь правилами и считать, что слова разделяются пробелами, то так:

fmap (unwords . foldM (map . flip (:)) [] . reverse . words) getLine >>= putStrLn

Если быть честным, то как-то так:

import Control.Monad

combinations = foldM (map . flip (:)) [] . reverse

main = fmap (unlines . combinations . lines) getContents >>= putStr

[User Picture]From: [info]migmit
2008-03-16 08:51 pm (UTC)

Re: Первая (Haskell)

(Link)

Я тя умоляю.
combinations = sequence
[User Picture]From: [info]_navi_
2008-03-13 09:00 pm (UTC)

rant и вторая (Haskell)

(Link)

Слова на входе и на выходе оканчиваются переводом строки, этот символ не является частью слова.
Я вот эту фразу по-разному могу понять: либо каждое слово оканчивается переводом строки, либо, как во втором примере, слова разделяются пробелами, а после уже перевод строки. Хотя, вообще говоря, так жёстко описывать формат входных/выходных данных мне не очень нравится: суть задач врядли состоит в том, чтобы прочитать несколько по-особенному разделённых слов и как-то по-особенному же их вывести, к тому же хорошо это специфицировать не так просто. Ясно, что и это надо уметь, но так от главного отвлекаешься на попытки расшифровать, что же имелось в виду.

Вторую особо красиво не получилось написать, ну да я не заморачивался. С эффективностью: надо думать, можно ли сильно лучше.

permutations xs = permutations' [] xs
  where
    permutations' [] [] = [[]]
    permutations' _  [] = []
    permutations' bs (x:xs) = (map (x:) $ permutations (bs ++ xs)) ++ (permutations' (x:bs) xs)

main = fmap (unlines . permutations) getLine >>= putStr

[User Picture]From: [info]ivan_ghandhi
2008-03-13 11:21 pm (UTC)

(Link)

From: [info]zhuzh
2008-03-14 04:30 pm (UTC)

(Link)

а исходники?
[User Picture]From: [info]pilpilon
2008-03-13 11:24 pm (UTC)

(Link)

import sys

def func(lis):
	a,lis = lis[0],lis[1:]
	return ["".join([i,k]) for i in a for k in func(lis)] if lis else list(a)

if __name__ == "__main__":
	print func(sys.argv[1:])



import sys

def func(lis):
	return [ i+k for i in lis for k in func(lis.replace(i, ""))] if len(lis) > 1 else lis

if __name__ == "__main__":
	print func(sys.argv[1])
[User Picture]From: [info]pilpilon
2008-03-13 11:35 pm (UTC)

(Link)

во избежание придирок, в первом случае sys.argv[1:] можно заменить на raw_input().split(), во втором sys.argv[1] на raw_input(), грубо говоря.
[User Picture]From: [info]palm_mute
2008-03-14 09:09 am (UTC)

(Link)

Если я правильно понял условие задачи, то
combinations решается как-то так:
import Control.Monad

combinations = sequence . lines
main = putStr . unlines . combinations =<< getContents

From: [info]zhuzh
2008-03-14 12:00 pm (UTC)

(Link)

отлично
[User Picture]From: [info]palm_mute
2008-03-14 09:35 am (UTC)

permutations

(Link)

что-то длинновато получилось

insertions x [] = [[x]]
insertions x (y:ys) = (x:y:ys) : map (y:) (insertions x ys)

permutations [] = [[]]
permutations (x:xs) = concatMap (insertions x) (permutations xs)

main = putStr . unlines . permutations =<< getLine

[User Picture]From: [info]deni_ok
2008-03-15 07:20 pm (UTC)

Re: permutations

(Link)

Та же идея на list comprehension:
splits xs = inits xs `zip` tails xs

permutations []     = [[]] 
permutations (x:xs) = [ps ++ x:qs | rs <- permutations xs, 
                                    (ps, qs) <- splits rs] 
Вроде короче не выходит, ещё и Data.List импортировать надо для inits и tails. Или явно определить
splits []         = [([], [])] 
splits xxs@(x:xs) = ([], xxs):[(x:ps, qs) | (ps, qs) <- splits xs]
[User Picture]From: [info]gianthare
2008-03-16 01:33 pm (UTC)

А я не постесняюсь решения в лоб выложить

(Link)

Combinations
#!/usr/bin/perl

while(<>) {
  chop;
  push @lines,$_;
}

&combine('',@lines);

sub combine {
  my $word = shift;
  if(0 == scalar(@_)) {
    print $word."\n";
    return;
  }
  my $line = shift;
  for my $l (split //,$line) {
    &combine($word.$l,@_);
  }
}

Permutations
#!/usr/bin/perl

$_ = <>;
chop;
my @word = split //,$_;

print STDERR "Permuting @word\n";

&perm('',@word);

sub perm {
  my $cur = shift;
  my @word = @_;

  if(0 == scalar(@word)) {
    print $cur."\n";
    return;
  }

  for(my $i = 0; $i <= $#word; $i++) {
    my $t = $word[$i];
    $word[$i] = $word[0];
    $word[0] = $t;

    &perm($cur.$word[0],@word[1..$#word]);

  }
}
[User Picture]From: [info]gianthare
2008-03-16 03:02 pm (UTC)

А вот комбинации stripped down to the bones, тскзть

(Link)

#!/usr/bin/perl
sub combine {
$#_? do { combine($_[0].$_,@_[2..$#_]) for @{$_[1]}; } : print $_[0]."\n";
}
combine('',map {chop; [split //];} (<>));

По непонятности и краткости уже может конкурировать с Хаскелем, по-моему
[User Picture]From: [info]ilya_dogolazky
2008-03-16 02:26 pm (UTC)

перестановки

(Link)

#include <cstdio>
#define swap(k,i) {int tmp=a[k]; a[k]=a[i]; a[i] = tmp;}
char a[101] ;
int N = 0;
void shuffle(int k)
{
  if(k<N-1)
    for(int i=k; i<N; ++i)
    {
      swap(k, i) ;
      shuffle(k+1) ;
      swap(k, i) ;
    }
  else
    printf("%s\n", a) ;
}
int main()
{
  for(int ch ; (ch=getchar())!=EOF && ch!='\n'; ++N)
    a[N] = ch ;
  a[N] = '\0' ;
  if(N<=1)
    printf("%s\n", a) ;
  else
    shuffle(0) ;
  return 0 ;
}

[User Picture]From: [info]gianthare
2008-03-16 03:04 pm (UTC)

Как же shuffle, когда permute

(Link)

Кстати, насколько я помню, должно работать и без второго swap. А отдельная обработка единичной длины действительно необходима?
[User Picture]From: [info]mungobungo
2008-03-17 11:41 am (UTC)

Simply J

(Link)

NB. Наверняка можно и короче, но для этого надо думать :)
permuts =. 3 : '((i.@! A. i.) # y.) { y.'

------------------------------------------

Результат работы
permuts 'abc'
abc
acb
bac
bca
cab
cba

А по первой части пока не вкурил условие, буду думать.
[User Picture]From: [info]gianthare
2008-03-17 11:59 am (UTC)

Re: Simply J

(Link)

О! А вот и APL. Только придется вам объяснить, что здесь делается, а то, боюсь, J мало кто читает
[User Picture]From: [info]kodt_rsdn
2008-03-17 12:21 pm (UTC)

(Link)

Про перестановки: требование уникальности необязательно - алгоритм Кнута (доступный в С++ как std::next_permutation()) корректно переставляет и с дубликатами.
#include <algorithm>
#include <string>
#include <iostream>

using namespace std;
int main()
{
  string word;
  cin >> word;
  sort(word.begin(), word.end());
  do
  {
    cout << word;
  }
  while(next_permutation(word.begin(), word.end());
}

[User Picture]From: [info]kodt_rsdn
2008-03-17 12:49 pm (UTC)

(Link)

Комбинации: то есть, нужно построить декартово произведение.
P = S1*S2*...*Sn

Pk = Sk*Sk+1...*Sn
Pk = Sk*Pk+1
Здесь слева от * - множество символов, а справа - множество векторов.

Традиционно для хаскела, и множества, и векторы представлены списками.
Произведение выглядит так
mult cs vs = [ c:v | c<-cs, v<-vs ] -- для каждого символа и каждого вектора выполнить конкатенацию

Любители могут выразить это через монадные комбинаторы.

Ну а произведение всех множеств - это, очевидно свёртка их списка.
combinations ws = foldr mult [[]] ws

Единственная хитрость - нуль-произведение - это не пустое множество, а множество из единственного нульмерного вектора.

Теперь только осталось присобачить ко вводу-выводу.

Advertisement