Уменьшенные перестановки

Рассмотрим следующую строку

abcd

Я могу вернуть 2 перестановки символов (декартово произведение), как это

$ echo {a,b,c,d}{a,b,c,d}
aa ab ac ad ba bb bc bd ca cb cc cd da db dc dd

Однако я хотел бы удалить избыточные записи, такие как

ba ca cb da db dc

и неверные записи

aa bb cc dd

так что я остался с

ab ac ad bc bd cd

Пример


person Steven Penny    schedule 15.06.2014    source источник


Ответы (5)


Вот чистый Баш:

#!/bin/bash

pool=( {a..d} )
for((i=0;i<${#pool[@]}-1;++i)); do
    for((j=i+1;j<${#pool[@]};++j)); do
        printf '%s\n' "${pool[i]}${pool[j]}"
    done
 done

и еще один:

#!/bin/bash

pool=( {a..d} )
while ((${#pool[@]}>1)); do
    h=${pool[0]}
    pool=("${pool[@]:1}")
    printf '%s\n' "${pool[@]/#/$h}"
done

Их можно записать в виде функций (или скриптов):

get_perms_ordered() {
    local i j
    for((i=1;i<"$#";++i)); do
        for((j=i+1;j<="$#";++j)); do
            printf '%s\n' "${!i}${!j}"
        done
     done
}

or

get_perms_ordered() {
    local h
    while (("$#">1)); do
        h=$1; shift
        printf '%s\n' "${@/#/$h}"
    done
}

Использовать как:

$ get_perms_ordered {a..d}
ab
ac
ad
bc
bd
cd

Это последнее можно легко преобразовать в рекурсивную функцию для получения упорядоченных перестановок заданной длины (без замены - я использую глупый словарь вероятностей шаровой урны), например,

get_withdraws_without_replacement() {
    # $1=number of balls to withdraw
    # $2,... are the ball "colors"
    # return is in array gwwr_ret
    local n=$1 h r=()
    shift
    ((n>0)) || return
    ((n==1)) && { gwwr_ret=( "$@" ); return; }
    while (("$#">=n)); do
        h=$1; shift
        get_withdraws_without_replacement "$((n-1))" "$@"
        r+=( "${gwwr_ret[@]/#/$h}" )
    done
    gwwr_ret=( "${r[@]}" )
}

Затем:

$ get_withdraws_without_replacement 3 {a..d}
$ echo "${gwwr_ret[@]}"
abc abd acd bcd
person gniourf_gniourf    schedule 15.06.2014

Вы можете использовать awk для фильтрации ненужных записей:

echo {a,b,c,d}{a,b,c,d} | awk -v FS="" -v RS=" " '$1 == $2 { next } ; $1 > $2 { SEEN[ $2$1 ] = 1 ; next } ; { SEEN[ $1$2 ] =1 } ; END { for ( I in SEEN ) { print I } }'

Подробно:

echo {a,b,c,d}{a,b,c,d} \
| awk -v FS="" -v RS=" " '

   # Ignore identical values
   $1 == $2 { next }

   # Reorder and record inverted entries
   $1 > $2  { SEEN[ $2$1 ] = 1 ; next }

            # Record everything else
            { SEEN[ $1$2 ] = 1 }

   # Print the final list
   END      { for ( I in SEEN ) { print I } }
 '

FS="" сообщает awk, что каждый символ является отдельным полем. RS=" " использует пробелы для разделения записей.

person Community    schedule 15.06.2014

Я уверен, что кто-то собирается сделать это в одной строке awk, но вот что-то в bash:

#!/bin/bash
seen=":"
result=""

for i in "$@"
do
    for j in "$@"
    do
        if [ "$i" != "$j" ]
        then
            if [[ $seen != *":$j$i:"* ]]
            then
                result="$result $i$j"
                seen="$seen$i$j:"
            fi
        fi
    done
done
echo $result

Выход:

$ ./prod.sh a b c d
ab ac ad bc bd cd

$ ./prod.sh I have no life
Ihave Ino Ilife haveno havelife nolife
person John C    schedule 15.06.2014
comment
это в bash или sh? есть что-то несовместимое между тем, что вы говорите, и вашей болтовней. - person gniourf_gniourf; 15.06.2014
comment
@gniourf_gniourf Я проверил это как есть, так что это ш. Однако он также работает в bash. - person John C; 15.06.2014
comment
Да, но в bash вы можете избавиться от grep с помощью if [[ $seen != *":$j$i:"* ]] .... Кроме того, используйте больше кавычек и используйте "$@" вместо $*. - person gniourf_gniourf; 15.06.2014

вот псевдокод для этого, основанный на ваших ограничениях и использующий массив для ваших символов:

for (i=0;i<array.length;i++)
{
    for (j=i+1;j<array.length;j++)
    {
        print array[i] + array[j]; //concatenation
    }
}
person luisluix    schedule 15.06.2014

Я понял, что ищу не перестановки, а набор мощности. Вот реализация в Awk:

{
  for (c = 0; c < 2 ^ NF; c++) {
    e = 0
    for (d = 0; d < NF; d++)
      if (int(c / 2 ^ d) % 2) {
        printf "%s", $(d + 1)
      }
    print ""
  }
}

Вход:

a b c d

Выход:

a
b
ab
c
ac
bc
abc
d
ad
bd
abd
cd
acd
bcd
abcd

Пример

person Steven Penny    schedule 30.12.2016