Algorithm 有趣的排序问题

Algorithm 有趣的排序问题,algorithm,sorting,Algorithm,Sorting,按特定顺序有1、0和U。(例如,“1001U0011”)1和0的数量相同,并且总是有两个“U”相邻。您可以将这对“U”与任何一对相邻的数字交换。下面是一个移动示例: __ / \ 1100UU0011 --> 11001100UU 任务是把所有的0放在1之前 以下是一个示例解决方案: First step: __ / \ 1100UU0011 Second step: ____ / \ UU00110011 000011UU11 --&g

按特定顺序有1、0和U。(例如,“1001U0011”)1和0的数量相同,并且总是有两个“U”相邻。您可以将这对“U”与任何一对相邻的数字交换。下面是一个移动示例:

      __
     /  \
1100UU0011 --> 11001100UU
任务是把所有的0放在1之前

以下是一个示例解决方案:

First step:
  __
 /  \
1100UU0011

Second step:
  ____
 /    \
UU00110011

000011UU11  --> DONE
创建蛮力算法非常容易。但是,像我的例子一样,解决一个简单的问题需要数百甚至数千个步骤。所以我在寻找更“聪明”的算法


这不是家庭作业;这是比赛中的一项任务。比赛结束了,但我找不到解决办法

编辑:这里的任务是创建一个能够对这些0和1进行排序的算法,而不仅仅是输出n0s和n1s以及2us。您必须以某种方式显示步骤,如我的示例中所示

编辑2:任务没有要求移动最少或类似的结果。但就我个人而言,我希望看到一个算法提供:)

如果A是0的数量,A也是1的数量,U是我们的数量:

for(int i=0; i<A; i++)
   data[i] = '0';
for(int i=0; i<A; i++)
   data[A+i] = '1';
for(int i=0; i<U; i++)
   data[A+A+i] = 'U';
for(int i=0;i只有两个Us

为什么不计算0的数量并存储美国的位置:

numberOfZeros = 0
uPosition = []
for i, value in enumerate(sample):
    if value = 0:
        numberOfZeros += 1
    if value = U
       uPosition.append(i)

result = []
for i in range(len(sample)):
    if i = uPosition[0]
       result.append('U')
       uPosition.pop(0)
       continue
    if numberOfZeros > 0:
       result.append('0')
       numberOfZeros -= 1
       continue
    result.append('1')
将导致运行时为O(n)

或者更好:

result = []
numberOfZeros = (len(sample)-2)/2
for i, value in enumerate(sample):
    if value = U
       result.append('U')
       continue
    if numberOfZeros > 0:
       result.append(0)
       numberOfZeros -= 1
       continue
    result.append(1)

首先,我想到的是自上而下的动态规划方法。这很容易理解,但会消耗大量内存。当我尝试应用自下而上的方法时,您可以尝试以下方法:

想法很简单-缓存暴力搜索的所有结果。它将变成这样:

function findBestStep(currentArray, cache) {
    if (!cache.contains(currentArray)) {
        for (all possible moves) {
            find best move recursively
        }
        cache.set(currentArray, bestMove);
    } 

    return cache.get(currentArray);
}
这个方法的复杂度是…O(2^n),这是令人毛骨悚然的。但是我认为没有任何逻辑方法可以使它更小,因为任何移动都是允许的

如果找到一种应用自底向上算法的方法,它可能会快一点(不需要缓存),但仍然会有O(2^n)复杂度

添加: 好的,我已经用Java实现了这个东西。代码很长,就像Java中一样,所以不要害怕它的大小。主算法非常简单,可以在底部找到。我认为没有任何方法可以比这更快(如果可以更快的话,这更像是一个数学问题).它消耗了大量内存,但计算速度仍然相当快。 这
0,1,0,1,0,1,0,1,0,1,0,1,1,1,2
在1秒内进行计算,占用~60mb内存,导致7步排序

public class Main {

    public static final int UU_CODE = 2;

    public static void main(String[] args) {
        new Main();
    }

    private static class NumberSet {
        private final int uuPosition;
        private final int[] numberSet;
        private final NumberSet parent;

        public NumberSet(int[] numberSet) {
            this(numberSet, null, findUUPosition(numberSet));
        }

        public NumberSet(int[] numberSet, NumberSet parent, int uuPosition) {
            this.numberSet = numberSet;
            this.parent = parent;
            this.uuPosition = uuPosition;
        }

        public static int findUUPosition(int[] numberSet) {
            for (int i=0;i<numberSet.length;i++) {
                if (numberSet[i] == UU_CODE) {
                    return i;
                }
            }
            return -1;
        }

        protected NumberSet getNextNumberSet(int uuMovePos) {
            final int[] nextNumberSet = new int[numberSet.length];
            System.arraycopy(numberSet, 0, nextNumberSet, 0, numberSet.length);
            System.arraycopy(this.getNumberSet(), uuMovePos, nextNumberSet, uuPosition, 2);
            System.arraycopy(this.getNumberSet(), uuPosition, nextNumberSet, uuMovePos, 2);
            return new NumberSet(nextNumberSet, this, uuMovePos);
        }

        public Collection<NumberSet> getNextPositionalSteps() {
            final Collection<NumberSet> result = new LinkedList<NumberSet>();

            for (int i=0;i<=numberSet.length;i++) {
                final int[] nextNumberSet = new int[numberSet.length+2];
                System.arraycopy(numberSet, 0, nextNumberSet, 0, i);
                Arrays.fill(nextNumberSet, i, i+2, UU_CODE);
                System.arraycopy(numberSet, i, nextNumberSet, i+2, numberSet.length-i);
                result.add(new NumberSet(nextNumberSet, this, i));
            }
            return result;
        }

        public Collection<NumberSet> getNextSteps() {
            final Collection<NumberSet> result = new LinkedList<NumberSet>();

            for (int i=0;i<=uuPosition-2;i++) {
                result.add(getNextNumberSet(i));
            }

            for (int i=uuPosition+2;i<numberSet.length-1;i++) {
                result.add(getNextNumberSet(i));
            }

            return result;
        }

        public boolean isFinished() {
            boolean ones = false;
            for (int i=0;i<numberSet.length;i++) {
                if (numberSet[i] == 1)
                    ones = true;
                else if (numberSet[i] == 0 && ones)
                    return false;
            }
            return true;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final NumberSet other = (NumberSet) obj;
            if (!Arrays.equals(this.numberSet, other.numberSet)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 83 * hash + Arrays.hashCode(this.numberSet);
            return hash;
        }

        public int[] getNumberSet() {
            return this.numberSet;
        }

        public NumberSet getParent() {
            return parent;
        }

        public int getUUPosition() {
            return uuPosition;
        }
    }

    void precacheNumberMap(Map<NumberSet, NumberSet> setMap, int length, NumberSet endSet) {
        int[] startArray = new int[length*2];
        for (int i=0;i<length;i++) startArray[i]=0;
        for (int i=length;i<length*2;i++) startArray[i]=1;
        NumberSet currentSet = new NumberSet(startArray);

        Collection<NumberSet> nextSteps = currentSet.getNextPositionalSteps();
        List<NumberSet> nextNextSteps = new LinkedList<NumberSet>();
        int depth = 1;

        while (nextSteps.size() > 0) {
            for (NumberSet nextSet : nextSteps) {
                if (!setMap.containsKey(nextSet)) {
                    setMap.put(nextSet, nextSet);
                    nextNextSteps.addAll(nextSet.getNextSteps());
                    if (nextSet.equals(endSet)) {
                        return;
                    }
                }
            }
            nextSteps = nextNextSteps;
            nextNextSteps = new LinkedList<NumberSet>();
            depth++;
        }
    }

    public Main() {
        final Map<NumberSet, NumberSet> cache = new HashMap<NumberSet, NumberSet>();
        final NumberSet startSet = new NumberSet(new int[] {0,1,0,1,0,1,0,1,0,1,0,1,0,1,2,2});

        precacheNumberMap(cache, (startSet.getNumberSet().length-2)/2, startSet);

        if (cache.containsKey(startSet) == false) {
            System.out.println("No solutions");
        } else {
            NumberSet cachedSet = cache.get(startSet).getParent();
            while (cachedSet != null && cachedSet.parent != null) {
                System.out.println(cachedSet.getUUPosition());
                cachedSet = cachedSet.getParent();
            }
        }
    }
}
公共类主{
公共静态最终int UU_代码=2;
公共静态void main(字符串[]args){
新的Main();
}
私有静态类编号集{
私人最终职位;
私人最终整数[]数字集;
私人最终号码设置家长;
公共数字集(int[]数字集){
这个(numberSet,null,findUUPosition(numberSet));
}
公共号码集(int[]号码集,号码集父项,int位置){
this.numberSet=numberSet;
this.parent=parent;
this.uuPosition=uuPosition;
}
公共静态int findUUPosition(int[]numberSet){

对于(int i=0;i我认为这应该有效:

  • 迭代一次以找到 如果他们不占领最后一个 两个点,把它们移过去 与最后两个进行交换
  • 创建一个 变量来跟踪当前 已排序的元素,最初设置为 array.length-1,表示任何内容 在它被分类之后
  • 迭代 向后。每次你遇到 1:
    • 将1及其前面的元素替换为U
    • 将U调回当前已排序元素跟踪器-1,更新变量
  • 继续,直到数组开始

如果你使用宽度优先的蛮力,它仍然是蛮力,但如果有答案的话,至少你可以保证想出最短的移动顺序。下面是一个使用宽度优先搜索的快速Python解决方案

从时间导入时间
def生成(c):
sep=“UU”
c1,c2=c.分裂(九月)
对于范围内的(透镜(c1)-1):
收益率c1[0:a]+sep+c1[(a+2):]+c1[a:(a+2)]+c2
对于范围内的(透镜(c2)-1):
收益率c1+c2[a:(a+2)]+c2[0:a]+sep+c2[(a+2):]
def解算(移动,使用):
已解决=[cl for cl in moves if cl[-1]。rindex('0')0:返回已解决[0]
返回解算([cl+[d]用于生成(cl[-1])中d的移动中的cl,如果d未使用且未使用。添加(d)],已使用)
代码=原始输入('输入代码:')
a=时间()
打印解算([[code]],set())
打印“经过的时间:,(time()-a),“秒”
以下是一个尝试:

Start:
  let c1 = the total number of 1s
  let c0 = the total number of 0s
  if the UU is at the right end of the string, goto StartFromLeft
StartFromRight
  starting at the right end of the string, move left, counting 1s, 
  until you reach a 0 or the UU.  
  If you've reached the UU, goto StartFromLeft.
  If the count of 1s equals c1, you are done.  
  Else, swap UU with the 0 and its left neighbor if possible.  
  If not, goto StartFromLeft.
StartFromLeft
  starting at the left end of the string, move right, counting 0s,
  until you reach a 1 or the UU.
  If you've reached the UU, goto StartFromRight.
  If the count of 0s equals c0, you are done.
  Else, swap UU with the 1 and its right neighbor, if possible.  
  If not, goto StartFromRight
  Then goto StartFromRight.
因此,对于原来的1100UU0011:

1100UU0011 - original
110000UU11 - start from right, swap UU with 00
UU00001111 - start from left, swap UU with 11
对于棘手的0101U01

0101UU01 - original
0UU11001 - start from right, can't swap UU with U0, so start from left and swap UU with 10
00011UU1 - start from right, swap UU with 00

然而,这并不能解决像01UU0这样的问题…但这可以通过一个标志来解决-如果你已经完成了整个算法一次,没有进行交换,它还没有解决…做点什么。

这是一个非常有趣的问题-所以让我们试着解决它。我将从对问题的精确分析开始,看看能发现什么。我会的在接下来的几天里,一件一件地添加到这个答案中。欢迎提供任何帮助

大小
n
的问题是一个精确的
n
零、
n
一和两个
U
s的问题,因此它由
2n+2
符号组成

(2n)!
-----
(n!)²
精确的
n
零和
n
一的不同序列。然后存在插入两个
U
s的
2n+1
可能位置,因此存在

(2n)!         (2n+1)!
-----(2n+1) = -------
(n!)²          (n!)²
大小
n
的问题实例

接下来,我正在寻找一种方法,为每个问题实例分配一个分数,以及该分数在所有可能的动作下是如何变化的,希望找出所需动作的最小数量

大小为1的实例已排序

--01   0--1   01--
(我想我会使用连字符而不是
U
s,因为它们
--10  ==only valid move==>  10--
-10-  no valid move
10--  ==only valid move==>  --10
// n >= 2, at least two zeros between -- and the 0/1 border
(0|a)--(0|b)00(1|n) => (0|n)--(1|n-2)11 => (0|n)(1|n)--
            ^^                       ^^
// n >= 3, one zero between -- and 0/1 boarder
(0|n-1)--01(1|n-1) => (0|n)1--(1|n-3)11 => (0|n)(1|n)--
         ^^                          ^^
// n >= 2, -- after last zero but at least two ones after --          
(0|n)(1|a)--(1|b)11 => (0|n)(1|n)--
                 ^^
// n >= 3, exactly one one after --
(0|n)(1|n-3)11--1 => (0|n)(1|n-3)--111 => (0|n)(1|n)--
            ^^                      ^^
// n >= 0, nothing to move
(0|n)(1|n)--
m_Steps = new Dictionary<string, List<string>>();
DoSort("UU1010011101", new List<string>);
Dictionary<string, List<string>> m_Steps = new Dictionary<string, List<string>>();

public void DoStep(string state, List<string> moves) {
 if (m_Steps.ContainsKey(state) && m_Steps[state].Count <= moves.Count + 1) // have better already
  return;

 // we have a better (or new) solution to get to this state, so set it to the moves we used to get here
 List<string> newMoves = new List<string>(moves);
 newMoves.Add(state);
 m_Steps[state] = newMoves;

 // if the state is a valid solution, stop here
 if (state.IndexOf('1') > state.LastIndexOf('0'))
  return;

 // try all moves
 int upos = state.IndexOf('U');
 for (int i = 0; i < state.Length - 1; i++) {
  // need to be at least 2 before or 2 after the UU position (00UU11 upos is 2, so can only move to 0 or 4)
  if (i > upos - 2 && i < upos + 2)
   continue;

  char[] chars = state.ToCharArray();
  chars[upos] = chars[i];
  chars[upos + 1] = chars[i + 1];
  chars[i] = chars[i + 1] = 'U';
  DoStep(new String(chars), newMoves);
 }
}

public void DoTests(int digits) { // try all combinations
 char[] chars = new char[digits + 2];
 for (int value = 0; value < (2 << digits); value++) {
  for (int uupos = 0; uupos < chars.Length - 1; uupos++) {
   for (int i = 0; i < chars.Length; i++) {
    if (i < uupos)
     chars[i] = ((value >> i) & 0x01) > 0 ? '1' : '0';
    else if (i > uupos + 1)
     chars[i] = ((value >> (i - 2)) & 0x01) > 0 ? '1' : '0';
    else
     chars[i] = 'U';
   }
   m_Steps = new Dictionary<string, List<string>>();
   DoSort(new string(chars), new List<string>);
   foreach (string key in m_Steps.AllKeys))
    if (key.IndexOf('1') > key.LastIndexOf('0')) { // winner
     foreach (string step in m_Steps[key])
      Console.Write("{0}\t", step);
     Console.WriteLine();
    }
  }
 }
}