最大化从集

时间:2015-06-04 02:11:27

标签: algorithm

我正在寻找一种能够将以下问题成功推广到n个集合的算法,但为简单起见,假设有4个不同的集合,每个集合包含4个元素。我们也可以假设每个集合总是包含相同数量的元素,但是可以有任意数量的元素。因此,如果第一组中有37个元素,我们可以假设每个其他组中也包含37个元素。

通过从第一组中取出1个元素并将其放入第一个位置,从第二个集合中取出1个元素并将其放在第二个位置来形成元素组合,依此类推。例如,假设第一组包含{A 0 ,A 1 ,A 2 ,A 3 },第二组包含{B 0 ,B 1 ,B 2 ,B 3 },第三组是{C < sub> 0 ,C 1 ,C 2 ,C 3 },第四个是{D 0 ,d <子> 1 ,d <子> 2 ,d <子> 3 }。一种可能的组合是[A 0 ,B 2 ,C 1 ,D 3 ]。

目标是在循环通过所有可能的组合时找到最大化距离的路径,尽可能避免重复。避免重复适用于连续组和单个列。例如:


  1. 个别专栏

    1. [A 0 ,B 0 ,C 0 ,D 0 ]
    2. [A 1 ,B 1 ,C 1 ,D 1 ]
    3. [A 2 ,B 0 ,C 2 ,D 2 ]
  2. 这是不正确的,因为B 0 比它必须更早重复。


    1. 连续小组

      1. [A 0 ,B 0 ,C 0 ,D 0 ]
      2. [A 1 ,B 1 ,C 1 ,D 1 ]
      3. [A 2 ,B 2 ,C 2 ,D 2 ]
      4. [A 3 ,B 3 ,C 3 ,D 3 ]
      5. [A 0 ,B 0 ,C 1 ,D 2 ]
    2. 这是不正确的,因为连续的对(A 0 ,B 0 )比它必须更早地重复。但是,如果最后一个是[A 0 ,B 1 ,C 0 ,D 1 ]那么这个没关系。


      当循环通过所有可能的组合时,必须重复连续的组,但目标是最大化它们之间的距离。因此,例如,如果使用(A 0 ,B 0 ),那么理想情况下,所有其他第一对将在再次使用之前使用。

      我能够找到一个解决方案,当有3套时,但我很难将它推广到n套,甚至解决4套。有什么想法吗?


      你可以发布三套解决方案吗?

      当然,首先我写下了所有可能的组合。然后我通过对不连续(第一和第三)元素重复的条目进行分组来制作三个3x3条目的矩阵:

      (A0,B0,C0)1, (A1,B0,C1)4, (A2,B0,C2)7    (A0,B0,C1)13, (A1,B0,C2)16, (A2,B0,C0)10    (A0,B0,C2)25, (A1,B0,C0)19, (A2,B0,C1)22
      (A0,B1,C0)8, (A1,B1,C1)2, (A2,B1,C2)5 (A0,B1,C1)11, (A1,B1,C2)14, (A2,B1,C0)17 (A0,B1,C2)23, (A1,B1,C0)26, (A2,B1,C1)20
      (A0,B2,C0)6, (A1,B2,C1)9, (A2,B2,C2)3 (A0,B2,C1)18, (A1,B2,C2)12, (A2,B2,C0)15 (A0,B2,C2)21, (A1,B2,C0)24, (A2,B2,C1)27

      然后我意识到如果我以对角线模式(由上标索引指示的顺序)遍历它将遵守规则。然后,我编写了以下代码以利用这种可视化模式:

      @Test
      public void run() {
      
          List<String> A = new ArrayList<String>();
          A.add("0");
          A.add("1");
          A.add("2");
          List<String> B = new ArrayList<String>();
          B.add("0");
          B.add("1");
          B.add("2");
          List<String> C = new ArrayList<String>();
          C.add("0");
          C.add("1");
          C.add("2");
      
          int numElements = A.size();
      
          List<String> output = new ArrayList<String>();
      
          int offset = 0;
          int nextOffset = 0;
      
          for (int i = 0; i < A.size()*B.size()*C.size(); i++) {
      
              int j = i % numElements;
              int k = i / numElements;
      
              if (j == 0 && k%numElements == numElements-1) {
                  nextOffset = (j+k+offset) % numElements;
              }
      
              if (j == 0 && k%numElements == 0) {
                  offset = nextOffset;
              }
      
              String first = A.get((j+k+offset) % numElements);
              String second = B.get(j);
              String third = C.get((j+k) % numElements);
      
              System.out.println(first + " " + second + " " + third);
              output.add(first + second + third);
          }
      
      }
      

      然而我只是意识到这也不理想,因为看起来这对(A 0 ,B 1 )过早重复,指数8和11 :(但是我想也许这是不可避免的,当从一个小组跨越到另一个小组时?这是一个难题!比看起来更难看


      如果您可以考虑并修改您的实际要求

      好的,所以我决定取消遍历所有可能组合的限制,而是稍微降低产量以提高结果的质量。

      这一点的重点是获取属于特定集合的元素并将它们组合以形成看似唯一的元素组合。因此,如果我从3个组合开始并且有3组,我可以将每个组合分成3个元素并将元素放入它们各自的集合中。然后我可以使用算法来混合和匹配元素,并产生27个看似独特的组合 - 当然它们是由衍生元素组成的,所以只要你看起来不太紧密,它们才会显得独特!

      因此手工形成的3种组合可以变成3 3 组合,节省了大量的时间和精力。当然,这也可以很好地扩展,如果我手动形成10个组合,那么算法可以生成1000个组合。我可能不需要那么多组合,所以我可以牺牲一些条目来更好地避免重复。特别是有3套我注意到虽然我的解决方案很不错,但是每个numElements 2 条目都发生了一些聚集。以下是3组5个元素的示例,在25个组合后有明显的重复:

      19)A1 B3 C1
      20)A2 B4 C2
      21)A4 B0 C4 &lt; -
      22)A0 B1 C0
      23)A1 B2 C1
      24)A2 B3 C2
      25)A3 B4 C3
      26)A0 B0 C4 &lt; -
      27)A1 B1 C0
      28)A2 B2 C1
      29)A3 B3 C2
      30)A4 B4 C3
      31)A1 B0 C0
      32)A2 B1 C1

      为了解决这个问题,我们可以引入以下语句并删除这个坏块:

      if (k % numElements == 0) continue;
      

      但是这只适用于numElements&gt; numSets,否则单个列规则将被破坏。 (如果你想知道我也在这个例子中改变了第一组和第三组的顺序,那么最初是这样做的,所以我没有用不好的重复打开)

      Aaannd我仍然完全坚持如何形成n或甚至4套的方法。它当然变得更加棘手,因为现在有不同大小的连续组可以避免,连续的三重奏以及配对。任何想法?我是否因为试图这样做而疯狂?

4 个答案:

答案 0 :(得分:1)

即使在你的问题修改后,我仍然不确定你想要什么。看起来你真正想要的是不可能的,但我不确定在条件下可以接受多少放松。不过,我会给它一个裂缝。

奇怪的是,似乎很少有文献(无论如何我能找到)涵盖你问题的主题,所以我不得不自己发明一些东西。这是一个想法:您正在寻找多维环面上的一系列点,使得序列的元素在复杂的度量中尽可能远。这让我想起的是多年前我在机械课上学到的东西,奇怪的是。如果在具有合理斜率的扁平环面上有一条线,则该线将在几个循环后循环回到自身,但如果您有一条具有不合理斜率的线,则该线将密集地覆盖整个圆环。

我不认为这对很多人来说意味着很多,但它确实给了我一个想法。每组的索引可能会以不合理的数量递增。当然,你必须发言,然后以模数为基础,但它似乎确实可以很好地覆盖基地,可以这么说。每一组的不合理步骤可能不同(并且相互不合理,使用相当松散的语言)。

为了使这个想法更精确,我写了一个简短的程序。请检查一下。

class Equidistributed {
  static final double IRRATIONAL1 = Math.sqrt(2);
  static final double IRRATIONAL2 = Math.sqrt(3);
  static final double IRRATIONAL3 = Math.sqrt(5)-1;

  // four sets of 7 elements each
  static int setSize = 7;

  public static void main(String[] args) {
    for (int i = 0; i < Math.pow(setSize,4); i++) {
      String tuple = "";
      int j = i % setSize;
      tuple += j + ",";
      j = ((int)Math.floor(i*IRRATIONAL1)) % setSize;
      tuple += j + ",";
      j = ((int)Math.floor(i*IRRATIONAL2)) % setSize;
      tuple += j + ",";
      j = ((int)Math.floor(i*IRRATIONAL3)) % setSize;
      tuple += j;
      System.out.println(tuple);
    }
  }
}

我“看了”结果,但它们并不完美,但它们非常好。此外,该计划运行迅速。这是四组具有可变数量的元素(我选择7作为示例)。我使用的无理数是基于素数的平方根;我从sqrt(5)中减去1,这样结果将在1到2之间。每个元组基本上都是

(i, floor(i*irrational1), floor(i*irrational2), floor(i*irrational3)) mod 7

统计上应该使序列均匀分布,这是你想要的结果。无论是否转化为正确的“距离”属性,我都无法确定。您应该编写一个程序来测试序列是否具有您想要的属性,然后将我的程序的输出传递给测试。

答案 1 :(得分:0)

假设值为1维,则无需比较每个元素之间的距离。相反,您可以在将每个集合与其他集合进行比较之前找到每个集合中的最大值和最小值。

步骤1:找到每个集合中具有最大值和最小值的元素(例如,A1,A34,B4,B32,C5,C40,在此示例中,数字越小,值越小)

步骤2:将A1与所有其他集的最大值进行比较,并对所有最小值重复该过程。

答案 2 :(得分:0)

定义所有可能组合的数组。 对于阵列的每个可能顺序,计算您的距离分数。如果大于先前的最佳值(默认start = 0),则将数组复制到输出中,覆盖先前的最佳数组。

答案 3 :(得分:0)

广义算法和编写代码来进行性能测试:

import java.util.*;

public class Solution {

   public static void main(String[] args) throws Exception {

      List<String> A = new ArrayList<>();
      A.add("A0"); A.add("A1"); A.add("A2"); 
      A.add("A3"); A.add("A4"); A.add("A5"); A.add("A6");

      List<String> B = new ArrayList<>();
      B.add("B0"); B.add("B1"); B.add("B2");
      B.add("B3"); B.add("B4"); B.add("B5"); B.add("B6");

      List<String> C = new ArrayList<>();
      C.add("C0"); C.add("C1"); C.add("C2"); 
      C.add("C3"); C.add("C4"); C.add("C5"); C.add("C6");

      List<String> D = new ArrayList<>();
      D.add("D0"); D.add("D1"); D.add("D2"); 
      D.add("D3"); D.add("D4"); D.add("D5"); D.add("D6");

      List<List<String>> columns = new ArrayList<>();
      columns.add(A); columns.add(B); columns.add(C); columns.add(D);

      List<String> output = equidistribute(columns);

//      for (String row : output) {
//         System.out.println(row);
//      }
//      new Solution().test(output, columns.size(), A.size());

      new Solution().testAllTheThings();

   }

   public static List<String> equidistribute(List<List<String>> columns) {

      List<String> output = new ArrayList<>();

      int[] primeNumbers = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
                           43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
                           101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
                           151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
                           199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
                           263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
                           317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
                           383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
                           443, 449, 457, 461, 463, 467, 479, 487, 491, 499,
                           503, 509, 521, 523, 541};

      int numberOfColumns = columns.size();
      int numberOfElements = columns.get(0).size();

      for (int i = 0; i < Math.pow(numberOfElements, numberOfColumns); i++) {

         String row = "";

         for (int j = 0; j < numberOfColumns; j++) {
            if (j==0) {
               row += columns.get(0).get(i % numberOfElements);
            } else {
               int index = ((int) Math.floor(i * Math.sqrt(primeNumbers[j-1]))) % numberOfElements;
               row += " " + columns.get(j).get(index);
            }
         }

         output.add(row);
      }

      return output;
   }

   class MutableInt {
      int value = 0;
      public void increment() { value++; }
      public int get() { return value; }
      public String toString() { return String.valueOf(value); }
   }

   public void test(List<String> columns, int numberOfColumns, int numberOfElements) throws Exception {

      List<HashMap<String, MutableInt>> pairMaps = new ArrayList<>();
      List<HashMap<String, MutableInt>> individualElementMaps = new ArrayList<>();

      // initialize structures for calculating min distance
      for (int i = 0; i < numberOfColumns; i++) {

         if (i != numberOfColumns-1) {
            HashMap<String, MutableInt> pairMap = new HashMap<>();
            pairMaps.add(pairMap);
         }

         HashMap<String, MutableInt> individualElementMap = new HashMap<>();
         individualElementMaps.add(individualElementMap);
      }

      int minDistancePair = Integer.MAX_VALUE;
      int minDistanceElement = Integer.MAX_VALUE;

      String pairOutputMessage = "";
      String pairOutputDebugMessage = "";
      String elementOutputMessage = "";
      String elementOutputDebugMessage = "";
      String outputMessage = numberOfColumns + " columns, " + numberOfElements + " elements";

      for (int i = 0; i < columns.size(); i++) {

         String[] elements = columns.get(i).split(" ");

         for (int j = 0; j < numberOfColumns; j++) {

            // pair stuff
            if (j != numberOfColumns-1) {

               String pairEntry = elements[j] + " " + elements[j+1];

               MutableInt count = pairMaps.get(j).get(pairEntry);

               if (pairMaps.get(j).containsKey(pairEntry)) {
                  if (count.get() <= minDistancePair) {
                     minDistancePair = count.get();
                     pairOutputMessage = "minDistancePair = " + minDistancePair;
                     pairOutputDebugMessage += "(" + pairEntry + " at line " + (i+1) + ")  min = " + minDistancePair + "\n";
                  }
                  count = null;
               }

               if (count == null) {
                  pairMaps.get(j).put(pairEntry, new MutableInt());
               }
            }

            // element stuff
            String elementEntry = elements[j];

            MutableInt count = individualElementMaps.get(j).get(elementEntry);

            if (individualElementMaps.get(j).containsKey(elementEntry)) {
               if (count.get() <= minDistanceElement) {
                  minDistanceElement = count.get();
                  elementOutputMessage = "minDistanceElement = " + minDistanceElement;
                  elementOutputDebugMessage += "(" + elementEntry + " at line " + (i+1) + ")  min = " + minDistanceElement + "\n";
               }
               count = null;
            }

            if (count == null) {
               individualElementMaps.get(j).put(elementEntry, new MutableInt());
            }

         }

         // increment counters
         for (HashMap<String, MutableInt> pairMap : pairMaps) {
            Iterator it = pairMap.entrySet().iterator();
            while (it.hasNext()) {
               Map.Entry mapEntry = (Map.Entry) it.next();
               ((MutableInt) mapEntry.getValue()).increment();
            }
         }
         for (HashMap<String, MutableInt> elementMap : individualElementMaps) {
            Iterator it = elementMap.entrySet().iterator();
            while (it.hasNext()) {
               Map.Entry mapEntry = (Map.Entry) it.next();
               ((MutableInt) mapEntry.getValue()).increment();
            }
         }

      }

      System.out.println(outputMessage + " -- " + pairOutputMessage + ", " + elementOutputMessage);
//      System.out.print(elementOutputDebugMessage);
//      System.out.print(pairOutputDebugMessage);


   }

   public void testAllTheThings() throws Exception {

      char[] columnPrefix = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
      int maxNumberOfColumns = columnPrefix.length;
      int maxNumberOfElements = 30;

      for (int i = 2; i < maxNumberOfColumns; i++) {

         for (int j = i; j < maxNumberOfElements; j++) {

            List<List<String>> columns = new ArrayList<>();

            for (int k = 0; k < i; k++) {
               List<String> column = new ArrayList<>();

               for (int l = 0; l < j; l++) {
                  column.add(String.valueOf(columnPrefix[k]) + l);
               }

               columns.add(column);
            }

            List<String> output = equidistribute(columns);
            test(output, i, j);

         }
      }

   }


}

编辑:删除每个集必须具有相同数量的元素的限制

public List<String> equidistribute(List<List<String>> columns) {

  List<String> output = new ArrayList<>();

  int[] primeNumbers = {  2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
                         43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
                        101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
                        151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
                        199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
                        263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
                        317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
                        383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
                        443, 449, 457, 461, 463, 467, 479, 487, 491, 499,
                        503, 509, 521, 523, 541};

  int numberOfColumns = columns.size();

  int numberOfCombinations = 1;
  for (List<String> column : columns) {
     numberOfCombinations *= column.size();
  }

  for (int i = 0; i < numberOfCombinations; i++) {

     String row = "";

     for (int j = 0; j < numberOfColumns; j++) {

        int numberOfElementsInColumn = columns.get(j).size();

        if (j==0) {
           row += columns.get(0).get(i % numberOfElementsInColumn);
        } else {
           int index = ((int) Math.floor(i * Math.sqrt(primeNumbers[j-1]))) % numberOfElementsInColumn;
           row += "|" + columns.get(j).get(index);
        }
     }

     output.add(row);
  }

  return output;
}