Tối ưu hóa gấp giấy để giảm thiểu vết mực


19

Mực đen sẫm đã vương vãi khắp tờ giấy trắng của bạn! Giải pháp rõ ràng là gấp tờ giấy sao cho các phần đen trắng gặp nhau và cả hai đều chuyển sang màu xám khi mực khuếch tán. Sau đó mở ra và bán lại cho đến khi giấy của bạn đều màu xám như nhau.

Tìm cách tốt nhất để thực hiện các nếp gấp này là nhiệm vụ của bạn trong thử thách mã hóa này. Pastebin này chứa bốn lưới có kích thước khác nhau gồm các số và số không. Mỗi lưới đại diện cho một mảnh giấy bắn tung tóe mà bạn phải chuyển sang màu xám. Số không là giấy và những cái là mực.

Trong các lưới này, chỉ các nếp gấp ngang và dọc dọc theo khoảng trống giữa các dòng và cột là hợp lệ. Khi một nếp gấp được thực hiện, các cặp giá trị chồng chéo được tính trung bình. Các nếp gấp được thực hiện cùng một lúc và luôn luôn mở ra. Các nếp gấp chỉ thay đổi phân phối mực, không phải kích thước của giấy.

Rn biểu thị gấp cạnh trái của lưới sang phải, bắt đầu sau cột thứ n. Đê biểu thị gấp cạnh trên của lưới xuống dưới, bắt đầu sau hàng thứ n. (n là 1 chỉ mục)

Thí dụ

Cho lưới này

0 1 1 1
0 0 0 0
0 0 0 0

nếp gấp D1 có nghĩa là "gấp toàn bộ hàng trên xuống rồi mở ra".

0 0.5 0.5 0.5
0 0.5 0.5 0.5
0   0   0   0

Sau đó, một R2 sẽ sản xuất

0.25 0.5 0.5 0.25
0.25 0.5 0.5 0.25
   0   0   0    0

và một R2 khác sẽ không thay đổi bất cứ điều gì.

Mục tiêu

Mục tiêu của bạn là viết một thuật toán tìm ra trình tự gấp mực tốt nhất cho mỗi trong bốn lưới sử dụng chính xác 8 lần mỗi lần. Các nếp gấp có thể là bất kỳ sự kết hợp nào của R hoặc D.

Chấm điểm

Điểm số của bài nộp của bạn là tổng số điểm của bạn cho mỗi lưới. Điểm của lưới là tổng của sự khác biệt tuyệt đối giữa mỗi giá trị của nó và trung bình của nó (tổng của nó chia cho diện tích của nó). Điểm thấp hơn là tốt hơn. Điểm 0 là hoàn hảo, nhưng có lẽ là không thể chỉ trong 8 lần.

Bạn phải báo cáo bốn trình tự gấp 8 bước với mã của bạn trong câu trả lời của bạn. Điều này là để chúng tôi có thể xác minh thuật toán của bạn thực sự hoạt động.

Vui lòng đặt chúng ở dạng này:

20*20R1D2R3D4R5D6R7D8
40*20R1D2R3D4R5D6R7D8
40*40R1D2R3D4R5D6R7D8
20*80R1D2R3D4R5D6R7D8

Đây là một kịch bản Python sẽ tính toán điểm số của bạn theo trình tự gấp của bạn.

Đương nhiên, bạn không nên sao chép trình tự trình tự của người khác. Chuỗi cho mỗi lưới chỉ thuộc về người đầu tiên tạo ra chúng.

Làm rõ

  • Lý tưởng nhất là thuật toán của bạn sẽ hoạt động tốt trên bất kỳ lưới nào, mặc dù bạn có thể điều chỉnh nó theo những cái cụ thể này.

  • Bạn phải gửi mã của bạn với trình tự của bạn. Để giành chiến thắng, bạn cần tập hợp các chuỗi gấp 8 bước nhỏ nhất chưa được đăng, và cũng là một thuật toán đứng trước sự giám sát của công chúng. Giải thích mã của bạn, đừng làm xáo trộn nó.

  • Lưới không bao giờ nên chứa số âm.

  • Tiêu chuẩn áp dụng.


1
Tôi nghĩ sẽ tốt hơn nếu bạn có một số trường hợp thử nghiệm và những người tham gia dự kiến ​​sẽ cung cấp mã tạo ra chuỗi, thay vì chỉ đưa ra chuỗi.
cần

1
Một lựa chọn khác là yêu cầu mọi người đưa ra chuỗi mà họ nhận được bằng mã của họ, nhưng yêu cầu họ cung cấp hàm băm (giả sử SHA-256) làm bằng chứng cho thấy họ thực sự sản xuất nó bằng công việc của chính họ. Tôi nhớ đã thấy loại cơ chế này một thời gian trước đây, nhưng tôi không thể nhớ. Bất cứ ai có thể chỉ ra thách thức đó?
cần

1
Một cách khác để cấm mã hóa cứng là làm cho thách thức mở ra cho các trường hợp thử nghiệm khác.
Howard

1
@ Calvin'sHob sở thích Tôi cũng thích một bộ các trường hợp thử nghiệm lớn hơn, bởi vì một số thuật toán có thể có giá tốt hơn trên các lưới nhất định so với các lưới khác. Những gì bạn có thể làm là những gì tôi đã làm với Vector Racing mà mỗi người tham gia có thể thêm trường hợp thử nghiệm vào bộ điểm chuẩn. Trong trường hợp đó, bạn phải đưa nó đi kiểm tra và chấm điểm tất cả các bài nộp, bởi vì bạn không thể mong đợi những người tham gia sớm sẽ chạy lại mã của họ với các trường hợp kiểm tra được thêm vào sau đó.
Martin Ender

1
@ Calvin'sHob sở hữu Lực lượng vũ phu là (19 + 39) ^ 8 (trừ một số đối xứng) khả thi hơn nhiều.
Howard

Câu trả lời:


8

Con trăn

Hoàn toàn cố gắng kết hợp các nếp gấp khác nhau trong vài lần đầu tiên, sau đó thực hiện các nếp gấp còn lại bằng cách sử dụng một cách tiếp cận tham lam.

Cách tiếp cận toàn diện được giới hạn trong phạm vi nếp gấp hợp lý ở trung tâm, sao cho nó sẽ không mất nhiều thời gian, trong khi không bỏ qua quá nhiều nếp gấp có thể mang lại mức tối thiểu tốt.

Ran sử dụng pypy trên macbook air của tôi.

Đáp án:

20*20D9R15R6D11R10R9D10R11
40*20D6D13D9R19R21R20D11D10
40*40D21R21R11D19R23R20D23D15
20*80D33D47D40R10D39D41R9R11

Đầu ra:

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.2
Time taken: 4.016076s
Score: 7.91125
20*20D9R15R6D11R10R9D10R11

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.2
Time taken: 28.529278s
Score: 16.34375
40*20D6D13D9R19R21R20D11D10

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.25
Time taken: 98.430465s
Score: 42.13
40*40D21R21R11D19R23R20D23D15

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.25
Time taken: 234.873787s
Score: 32.30875
20*80D33D47D40R10D39D41R9R11

Tổng số điểm: 7.91125 + 16.34375 + 42.13 + 32.30875 = 98,69375

Mã số:

import time, math
from collections import deque

numberOfFolds = 8 # Total number of folds

startTime = time.clock()

exec "grid = ("+"""
1 1 1 0 1 1 0 0 1 0 0 1 0 1 1 0 1 0 1 1
1 1 0 0 0 1 0 1 1 0 0 0 1 0 1 1 1 0 1 1
0 1 0 0 0 1 0 1 0 1 1 1 1 0 1 0 1 0 1 0
0 0 0 1 0 1 0 0 0 0 1 1 1 0 1 1 0 0 0 1
0 1 0 1 1 0 0 0 0 0 1 0 1 1 1 0 1 0 1 0
1 0 1 1 0 1 1 1 1 1 1 0 0 1 0 1 0 1 0 1
0 1 1 1 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0
1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 0 0 0 0 1
1 1 0 0 0 1 1 1 0 1 0 1 0 0 1 1 0 0 1 0
0 1 1 0 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1
0 1 1 1 1 0 0 1 1 0 1 0 1 1 1 1 0 1 1 0
0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 0 0 1
0 0 1 1 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1
1 1 1 1 0 1 1 0 0 0 0 1 1 1 0 0 0 0 0 1
1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0
0 1 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 0 1
0 0 1 0 1 1 1 1 0 1 1 0 1 0 1 0 0 1 1 0
0 1 1 0 1 0 0 1 0 0 1 1 1 1 1 0 1 1 0 0
0 0 1 0 1 1 1 0 0 1 0 0 0 1 0 1 1 1 1 1 
""".replace(" ",",").replace("\n","],[")[2:-2]+")"

def getAverage(grid):
    count = total = 0
    for j in grid:
        for i in j:
            count += 1
            total += i
    return total/float(count)

def getScore(grid, average):
    score = 0
    for j in grid:
        for i in j:
            score += abs(average-i)
    return score

def downFoldedGrid(grid, row, width, height, copy=True):
    if copy: grid = [r[:] for r in grid]
    foldRange = min(row, height-row)
    for j in xrange(foldRange):
        rowRef1 = grid[row+j]
        rowRef2 = grid[row-1-j]
        for i in xrange(width):
            rowRef1[i] = rowRef2[i] = (rowRef1[i] + rowRef2[i]) * .5
    return grid

def downFoldedScore(grid, score, average, row, width, height):
    foldRange = min(row, height-row)
    average2  = 2*average
    for j in xrange(foldRange):
        rowRef1 = grid[row+j]
        rowRef2 = grid[row-1-j]
        a = b = c = 0
        for i in xrange(width):
            a = rowRef1[i] 
            b = rowRef2[i]
            c = a+b
            score += abs(average2-c) - abs(average-a) - abs(average-b)
    return score

def rightFoldedGrid(grid, column, width, height, copy=True):
    if copy: grid = [r[:] for r in grid]
    foldRange = min(column, width-column)
    for j in xrange(height):
        rowRef = grid[j]
        for i in xrange(foldRange):
            a = column+i
            b = column-1-i
            rowRef[a] = rowRef[b] = (rowRef[a] + rowRef[b]) * .5
    return grid

def rightFoldedScore(grid, score, average, column, width, height):
    foldRange = min(column, width-column)
    average2 = 2*average
    for j in xrange(height):
        rowRef = grid[j]
        a = b = c = 0
        for i in xrange(foldRange):
            a = rowRef[column+i]
            b = rowRef[column-1-i]
            c = a+b
            score += abs(average2-c) - abs(average-a) - abs(average-b)
    return score

def bestFoldsGreedy(grid, average, maxFolds, width, height):
    score  = getScore(grid, average)
    folds  = []
    append = folds.append
    for z in xrange(maxFolds):
        bestFold      = 0
        bestFoldScore = score
        bestFoldGrid  = grid
        for i in xrange(1, width): #Try all right folds
            foldScore = rightFoldedScore(grid, score, average, i, width, height)
            if foldScore < bestFoldScore:
                bestFold      = i
                bestFoldScore = foldScore
        for i in xrange(1, height): #Try all down folds
            foldScore = downFoldedScore(grid, score, average, i, width, height)
            if foldScore < bestFoldScore:
                bestFold      = -i
                bestFoldScore = foldScore
        if bestFold:
            append(bestFold)
            score = bestFoldScore
            if bestFold > 0: rightFoldedGrid(grid, bestFold, width, height, False)
            else:            downFoldedGrid(grid, -bestFold, width, height, False)
    return score, folds


# Get the height and width
height  = len(grid)
width   = len(grid[0])

# Transpose the grid if height > width for better locality of reference
transposed = False
if height > width:
    grid = [[grid[i][j] for i in range(height)] for j in range(width)]
    transposed = True
    height, width = width, height

# The exhaustive grids and folds attempted
exhaustiveGridsAndFolds = deque([(grid,[])])
popleft = exhaustiveGridsAndFolds.popleft
append  = exhaustiveGridsAndFolds.append

# Set the bounds to exhaustively test for
exhaustiveLevels   = 3
prunePadding       = [0.2, 0.25][width*height > 1000]
leftBound          = int(max(width*prunePadding, 1))
rightBound         = int(width*(1.0-prunePadding))
topBound           = int(max(height*prunePadding, 1))
bottomBound        = int(height*(1.0-prunePadding))

# Populate the exhaustive grids and folds
while 1:
    grid, folds = popleft()
    if len(folds) == exhaustiveLevels:
        append((grid, folds))
        break
    for i in xrange(leftBound, rightBound):
        if i not in folds:
            append((rightFoldedGrid(grid, i, width, height), folds+[i]))
    for i in xrange(topBound, bottomBound):
        if -i not in folds:
            append((downFoldedGrid(grid, i, width, height), folds+[-i]))

# Test all the exhaustive grids and folds greedily
average             = getAverage(grid)
bestFinalScore      = getScore(grid, average)
bestFinalFolds      = []
numberOfGreedyFolds = numberOfFolds-exhaustiveLevels
while exhaustiveGridsAndFolds:
    grid, exhaustiveFolds = popleft()
    finalScore, greedyFolds = bestFoldsGreedy(grid, average, numberOfGreedyFolds, width, height)
    if finalScore <= bestFinalScore:
        bestFinalScore = finalScore
        bestFinalFolds = exhaustiveFolds + greedyFolds


# Repeat the last fold till the total number of folds if needed
if len(bestFinalFolds) < numberOfFolds:
    bestFinalFolds += [bestFinalFolds[-1]]*(numberOfFolds-len(bestFinalFolds))

# Print the best result
foldsString = ""
down  = "D"
right = "R"
if transposed:
    down,  right  = right,  down
    width, height = height, width
for fold in bestFinalFolds:
    if   fold > 0: foldsString += right+str(fold)
    elif fold < 0: foldsString += down+str(-fold)
print "Exhaustive folds levels: " + str(exhaustiveLevels)
print "Percentage pruned from sides from exhaustive folds: " + str(prunePadding)
print "Time taken: " + str(time.clock()-startTime) + "s"
print "Score: " + str(bestFinalScore)
print str(width) + "*" + str(height) + foldsString

2
Được rồi, tôi có thể ngừng làm việc này bây giờ. Đây sẽ là chính xác thuật toán của tôi.
Martin Ender

@bitpwner Bạn vẫn đang sử dụng 0,5 làm mức trung bình của lưới nhưng thực tế hơi khác nhau tùy theo lưới. Với kịch bản của tôi tại ideone.com/5wbrOQ, bạn đang chấm 8,26, 17,71875, 44,61125 và 32,72 cho tổng số 103,31.
Sở thích của Calvin

5

C, 16.344 (4 phút 33 giây)

Di chuyển tốt nhất được tìm thấy cho đến nay: D6, D13, R19, D9, D11, R21, D10, R20

Sử dụng hỗn hợp của Monte Carlo và leo đồi. Có thể được thực hiện để chạy nhanh hơn nhiều, tôi chắc chắn.

#include <stdio.h>
#include <stdlib.h>

/*

Best result so far: 16.344
D6,D13,R19,D9,D11,R21,D10,R20

real    4m33.027s
user    4m12.787s
sys 0m1.334s

*/

#define GRID_WIDTH   40
#define GRID_HEIGHT  20
#define GRID_SIZE    (GRID_WIDTH * GRID_HEIGHT)
#define NUM_FOLDS    8
#define MAX_VALUE    (1 << NUM_FOLDS)
#define TARGET_VALUE (MAX_VALUE / 2)

double score_grid(short *g) {
  int i, sum;
  for (i=sum=0; i<GRID_SIZE; i++) sum += abs(*g++ - TARGET_VALUE);
  return sum * 1.0 / MAX_VALUE;
}

void h_fold(short *g, int fold_row) {
  int x, y0, y1;
  if (fold_row<1 || fold_row>=GRID_HEIGHT) return;
  y1 = fold_row * GRID_WIDTH;
  y0 = y1 - GRID_WIDTH;
  while (y0>=0 && y1<GRID_SIZE) {
    for (x=0; x<GRID_WIDTH; x++) {
      g[y0+x] = g[y1+x] = (g[y0+x] + g[y1+x]) >> 1;
    }
    y0 -= GRID_WIDTH;
    y1 += GRID_WIDTH;
  }
}

void v_fold(short *g, int fold_col) {
  int y, x0, x1;
  if (fold_col<1 || fold_col>=GRID_WIDTH) return;
  x1 = fold_col;
  x0 = x1 - 1;
  while (x0>=0 && x1<GRID_WIDTH) {
    for (y=0; y<GRID_SIZE; y+=GRID_WIDTH) {
      g[y+x0] = g[y+x1] = (g[y+x0] + g[y+x1]) >> 1;
    }
    x0--;
    x1++;
  }
}

void print_grid(short *g) {
  int i=0, checksum=0;
  while (i<GRID_SIZE) {
    checksum += *g;
    printf("%3X",*g++);
    if ((++i) % GRID_WIDTH == 0) putchar('\n');
  }
  if (checksum != GRID_SIZE * TARGET_VALUE) printf("Bad total: %d\n",checksum);
}

void init_grid(short *g) {
  int i;
  static short *start_grid=0, *sg;
  if (!start_grid) {
    char *src = "11010110100011100000001000110001001101010111000100100100000101100000101111000010"
                "10110011111011111101101011111001000010101010110111000101000001011111101000011001"
                "10000111111001111011100101101001101100001110001101001011010011011110101000011100"
                "00110010100010100010110101001100110001100100111010000110100110001000110000111101"
                "01000001110000101000110101011011101010111110101010110000001011010010000011101000"
                "11111011111100100100100010111010111111000101011110000100111111111000110101101101"
                "00110100010111101111000011011010000110001001101010010101110010110111101001011111"
                "10110001101100001110010100110100010011011110100110000100100111101101000010011001"
                "00011100110100111101000000001000010100001101001011000101101001000100111100011010"
                "00010110001110011111100011101111011100111001110011111011010010000100101111101001";
    start_grid = malloc(GRID_SIZE * sizeof(short));
    for (i=0; i<GRID_SIZE; i++) start_grid[i] = (src[i]&1)<<NUM_FOLDS;
  }
  sg = start_grid;
  for (i=0; i<GRID_SIZE; i++) *g++ = *sg++;
}

double evaluate(int *moves) {
  short *grid;
  double score;
  int i, f;
  grid = malloc(GRID_SIZE * sizeof(short));
  init_grid(grid);
  for (i=0; i<NUM_FOLDS; i++) {
    f = moves[i];
    if (f>0) v_fold(grid,f);
    else h_fold(grid,-f);
  }
  score = score_grid(grid);
  free(grid);
  return score;
}


double optimize_folding(int *moves, double score) {
  int opt_cycle, i, which_fold, new_move, f1, f2, t;
  double s;

  for (opt_cycle=0; opt_cycle<1000; opt_cycle++) {
    for (i=0; i<NUM_FOLDS; i++) {
      which_fold = random() % NUM_FOLDS;
      do {
        if (random()&1) new_move = random() % (GRID_WIDTH-1) + 1;
        else new_move = -(random() % (GRID_HEIGHT-1) + 1);
      } while (moves[which_fold]==new_move);
      t = moves[which_fold];
      moves[which_fold] = new_move;
      s = evaluate(moves);
      if (s>score) moves[which_fold] = t;
      else score = s;
    }
    for (i=0; i<NUM_FOLDS; i++) {
      f1 = random() % NUM_FOLDS;
      do {
        f2 = random() % NUM_FOLDS;
      } while (f2==f1);
      t = moves[f1];
      moves[f1] = moves[f2];
      moves[f2] = t;
      s = evaluate(moves);
      if (s>score) {
        t = moves[f1];
        moves[f1] = moves[f2];
        moves[f2] = t;
      }
      else score = s;
    }
  }

  return score;
}

void show_moves(int *moves) {
  int i, m;
  for (i=0; i<NUM_FOLDS; i++) {
    m = moves[i];
    printf("%c%d%c",(m>0)?'R':'D',abs(m),((i+1)%NUM_FOLDS)?',':'\n');
  }
}

int main() {
  int i, j, moves[NUM_FOLDS], save_moves[NUM_FOLDS];
  double score, best_score = 1.0E+99;

  srandomdev();
  for (i=0; i<400; i++) {
    for (j=0; j<NUM_FOLDS; j++) {
            if (random()&1) moves[j] = random() % (GRID_WIDTH-1) + 1;
            else moves[j] = -(random() % (GRID_HEIGHT-1) + 1);
        }
        score = optimize_folding(moves, 1.0E+99);
        if (score<best_score) {
            best_score = score;
            for (j=0; j<NUM_FOLDS; j++) save_moves[j]=moves[j];
        }
    }
  printf("%.3lf\n",best_score);
  show_moves(save_moves);
  return 0;
}

Bah Chỉ cần lưu ý rằng câu hỏi đã thay đổi. Tôi sẽ phải sửa cái này sau ...
squossish ossifrage

Hiện tại tôi đang nhận được số điểm kha khá là 16,34375 cho 40 * 20 của bạn.
Sở thích của Calvin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.