加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

bash – 随机播放一个数组,使得没有两个相同的元素相邻

发布时间:2020-12-15 22:59:15 所属栏目:安全 来源:网络整理
导读:我有一个包含字符串的数组.其中几个字符串可以是相同的,也没关系.它们可以按任何顺序开始,但很可能是按字母顺序排列.我有以下shuffle函数,它将洗牌所有元素.但是,我想添加一个条件,即数组中没有两个相同的字符串可以相邻. 例如,这很好:ook eek ook monkey o
我有一个包含字符串的数组.其中几个字符串可以是相同的,也没关系.它们可以按任何顺序开始,但很可能是按字母顺序排列.我有以下shuffle函数,它将洗牌所有元素.但是,我想添加一个条件,即数组中没有两个相同的字符串可以相邻.

例如,这很好:ook eek ook monkey ook但是这不是:ook ook eek ook monkey,因为两个ook相邻.假设已经检查了输入,使得任何重复小于元素总数的一半,因此存在一组非相邻解.例如,ook ook ook eek将被拒绝.字符串可以包含空格和UTF-8字符,但不包含新行 – 字符串实际上是图像的文件名.

如何修改shuffle函数来实现这一目标?

或者有更好的方法吗?

shuffle() {
    # This function shuffles the elements of an array in-place using the
    # Knuth-Fisher-Yates shuffle algorithm. 
    local i tmp size max rand

    # $RANDOM % (i+1) is biased because of the limited range of $RANDOM
    # Compensate by using a range which is a multiple of the array size.
    size=${#array[*]}
    max=$(( 32768 / size * size ))
    for ((i=size-1; i>0; i--)); do
        while (( (rand=$RANDOM) >= max )); do :; done
            rand=$(( rand % (i+1) ))
            tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
    done
}

解决方法

给定拒绝前置条件,可以将单词列表分成几个“等价类”(EC) – 每个单词组中的特殊单词组无论采用何种标准都是相同的.拒绝意味着不超过一个EC部分位于列表的一半,部分位于另一半.

我们将这个EC的一部分放在一边,以便(1)剩下的部分包含在列表剩余的一半中,并且(2)两半的大小严格相等.然后我们将两个人分开洗牌.在那之后,我们合并它们,前半部分占据奇数元素,而evens则是下半部分.然后我们随机插入先前放置的剩余元素.它非常简单,因为它们都属于一个EC,因此很容易标记它们可以存在的位置和不能存在的位置.

通过构造,可以没有属于一个EC的两个相邻元件.

[编辑:]最后,执行上面的内容.

shuffle() {
    local sort="$(sort <<<"$1" | sed "s/^/+/g")"
    local size="$(grep -c ^ <<<"$sort")"
    local take cntr head tail

    if [ "$sort" == "${sort%%$'n'*}" ]; then
        # single line: nothing to shuffle
        echo "${sort#+}"
        return
    fi
    if [ $[size & 1] == 1 ]; then
        # enforcing equal halves,beginning to extract the middle
        take="$(head -$[size / 2 + 1] <<<"$sort" | tail -1)"
    fi
    cntr="$take"
    size=$[size / 2]
    head="$(head -$size <<<"$sort")"
    tail="$(tail -$size <<<"$sort")"
    while [ "$(head -1 <<<"$tail")" == "$(tail -1 <<<"$head")" ]; do
        # continue extracting the middle,if still left
        if [ -n "$cntr" -a "$cntr" != "$(tail -1 <<<"$head")" ]; then
            break
        else
            cntr="$(tail -1 <<<"$head")"
        fi
        ((--size))
        head="$(head -$size <<<"$head")"
        tail="$(tail -$size <<<"$tail")"
        take="${take:+$take$'n'}$cntr"$'n'"$cntr"
    done
    sort=()
    for cntr in $(seq $size); do
        # transforming two line sets into a single interlaced array
        sort[$[cntr * 4 - 3]]="$(head -$cntr <<<"$head" | tail -1)"
        sort[$[cntr * 4 - 1]]="$(head -$cntr <<<"$tail" | tail -1)"
    done
    for cntr in $(seq $[size - 1]); do
        # shuffling each of the interlaced halves by Fisher-Yates
        head="${sort[$[cntr * 4 - 3]]}"
        tail=$[RANDOM % (size - cntr + 1) + cntr]
        sort[$[cntr * 4 - 3]]="${sort[$[tail * 4 - 3]]}"
        sort[$[tail * 4 - 3]]="$head"
        head="${sort[$[cntr * 4 - 1]]}"
        tail=$[RANDOM % (size - cntr + 1) + cntr]
        sort[$[cntr * 4 - 1]]="${sort[$[tail * 4 - 1]]}"
        sort[$[tail * 4 - 1]]="$head"
    done
    if [ -n "$take" ]; then
        # got a remainder; inserting
        tail=($(seq 0 $[size * 2]))
        for cntr in $(seq $[size * 2]); do
            # discarding potential places with remainder in proximity
            if [ "${sort[$[cntr * 2 - 1]]}" 
              == "${take%%$'n'*}" ]; then
                tail[$[cntr - 1]]=""
                tail[$[cntr]]=""
            fi
        done
        tail=(${tail[@]})
        for cntr in $(seq 0 $[${#tail[@]} - 2]); do
            # shuffling the remaining places,also by Fisher-Yates
            head="${tail[$cntr]}"
            size=$[RANDOM % (${#tail[@]} - cntr) + cntr]
            tail[$cntr]="${tail[$size]}"
            tail[$size]="$head"
        done
        size="$(grep -c ^ <<<"$take")"
        while ((size--)); do
            # finally inserting remainders
            sort[$[${tail[$size]} * 2]]="${take%%$'n'*}"
        done
    fi
    head=0
    size="${#sort[@]}"
    while ((size)); do
        # printing the overall result
        if [ -n "${sort[$head]}" ]; then
            echo "${sort[$head]#+}"
            ((size--))
        fi
        ((head++))
    done
}

# a very simple test from the original post
shuffle 
"ook
ook
eek
ook
monkey"

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读