MDeath-Kid

- M I T & Y
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1.3 Crafting Winning Solutions - 翻译

Posted on 2011-11-14 21:38  MDeath-Kid  阅读(627)  评论(0)    收藏  举报

by MDK!2011/11/14 21:34


Crafting Winning Solutions

A good way to get a competitive edge is to write down a game plan for what you're going to do in a contest round. This will help you script out your actions, in terms of what to do both when things go right and when things go wrong. This way you can spend your thinking time in the round figuring out programming problems and not trying to figure out what the heck you should do next... it's sort of like precomputing your reactions to most situations.

一个好的方式去得到竞争优势是写一个游戏计划,写一写你在比赛的时候应该做什么。这将规范你的行为,当你失利或者有利的时候知道怎么做。同样你能够把你的思考时间放在想程序问题而不去想你下一步应该做什么。这就像你排序出来你下一步应该做什么。

Mental preparation is also important.

Game Plan For A Contest Round

Read through ALL the problems FIRST; sketch notes with algorithm, complexity, the numbers, data structs, tricky details, ...

  • Brainstorm many possible algorithms - then pick the stupidest that works!
  • DO THE MATH! (space & time complexity, and plug in actual expected and worst case numbers)
  • Try to break the algorithm - use special (degenerate?) test cases
  • Order the problems: shortest job first, in terms of your effort (shortest to longest: done it before, easy, unfamiliar, hard)

游戏ARound 计划:

通览所有的题目,迅速标记他们的算法,复杂度,数据量,数据结构,棘手的细节,。。。

  • 头脑风暴出很多可能的算法-然后选出最愚蠢但是能够通过的算法
  • 数学估计!(时空复杂性,算出实际上最坏的case的值)
  • 尝试找到你算法不可行的地方 - 用特殊的例子(退化?败类?变态?)
  • 管理那些problems:先做对你来说最先想到的(有低到高--简单,不熟悉,困难)

Coding a problem - For each, one at a time:

  • Finalize algorithm
  • Create test data for tricky cases
  • Write data structures
  • Code the input routine and test it (write extra output routines to show data?)
  • Code the output routine and test it
  • Stepwise refinement: write comments outlining the program logic
  • Fill in code and debug one section at a time
  • Get it working & verify correctness (use trivial test cases)
  • Try to break the code - use special cases for code correctness
  • Optimize progressively - only as much as needed, and keep all versions (use hard test cases to figure out actual runtime)
写代码 - 对每个问题,第一次解决一个
  • 最终确定的算法
  • 写一些tricky case
  • 写数据结构
  • 码 输入并且测试(写一下额外的输出来测试数据)
  • 码 输出并且测试
  • 逐步细化:写注释来概述程序的逻辑
  • 每次写一个部分并且测试
  • 让她能工作 & 验证正确性(用一些简单的案例)
  • 尝试break 代码 - 用你的变态数据测试代码的正确性
  • 逐步优化 - 仅仅根据需要来增加测试,并且keep all versions……(用硬盘测试出来实际的运行时间)
Time management strategy and "damage control" scenarios
Have a plan for what to do when various (foreseeable!) things go wrong; imagine problems you might have and figure out how you want to react. The central question is: "When do you spend more time debugging a program, and when do you cut your losses and move on?". Consider these issues:
  • How long have you spent debugging it already?
  • What type of bug do you seem to have?
  • Is your algorithm wrong?
  • Do you data structures need to be changed?
  • Do you have any clue about what's going wrong?
  • A short amount (20 mins) of debugging is better than switching to anything else; but you might be able to solve another from scratch in 45 mins.
  • When do you go back to a problem you've abandoned previously?
  • When do you spend more time optimizing a program, and when do you switch?
  • Consider from here out - forget prior effort, focus on the future: how can you get the most points in the next hour with what you have?
时间管理策略和“损害控制”的方案

有没有一个方案来准备各类(可预见的)出错的事情;想象一下你可能会出的问题并且想出你怎么去纠正。中心问题是:“当你花更多的时间来debugging程序,什么时候你能减少你浪费的时间并且继续前进?”。想想下面的问题:

  • 你通常debugging完一个程序要多久?
  • 通常那些bug你经常犯?
  • 你的算法错过吗?
  • 你的数据结构要换嘛?
  • 那个地方出错了你有没有线索-(头绪)?
  • 一个很短的调试(20min)比换成其他任何方法都强;但是你也有可能花45min从头解决掉这个问题。
  • 你什么时候回头看你曾放弃的题目?
  • 你什么时候花更多的时间来优化,或者什么时候放弃从新开始?
  • 考虑从现在转变 - 忘掉之前的成就,focus on the future:你如何能在接下来一个小时内取到更多的分数用你所拥有的?
Have a checklist to use before turning in your solutions:
    Code freeze five minutes before end of contest?
  • Turn asserts off.
  • Turn off debugging output.
在你改变你的解决问题的方式前,列一个清单
  • 比赛结束5min前你有没有冻结代码?
  • 关闭断言_百科
  • 关闭调试的输出
Tips & Tricks
  • Brute force it when you can
  • KISS: Simple is smart!
  • Hint: focus on limits (specified in problem statement)
  • Waste memory when it makes your life easier (if you can get away with it)
  • Don't delete your extra debugging output, comment it out
  • Optimize progressively, and only as much as needed
  • Keep all working versions!
  • Code to debug:
    • whitespace is good,
    • use meaningful variable names,
    • don't reuse variables,
    • stepwise refinement,
    • COMMENT BEFORE CODE.
  • Avoid pointers if you can
  • Avoid dynamic memory like the plague: statically allocate everything.
  • Try not to use floating point; if you have to, put tolerances in everywhere (never test equality)
  • Comments on comments:
    • Not long prose, just brief notes
    • Explain high-level functionality: ++i; /* increase the value of i by */ is worse than useless
    • Explain code trickery
    • Delimit & document functional sections
    • As if to someone intelligent who knows the problem, but not the code
    • Anything you had to think about
    • Anything you looked at even once saying, "now what does that do again?"
    • Always comment order of array indices
  • Keep a log of your performance in each contest: successes, mistakes, and what you could have done better; use this to rewrite and improve your game plan!
提示 & 技巧
  • 如果能暴力,do it!
  • KISS: 简单就是聪明
  • HINT: 注意限制(尤其是题目的描述)
  • 浪费内存吧,如果这能让你的life 很爽(当然如果能解决掉那个不爽的问题)
  • 不要删掉你的调试内容,注释掉他。
  • 逐步优化,仅当需要更多的优化时。
  • 保留所有好使的代码版本。
  • 代码调试:
    • 空格多多益善
    • 使用有意义的变量名
    • 不要重复使用变量,
    • 逐步细化,
    • 码 前注释.
  • 如果可能的话,避免指针。
  • 避免麻烦的动态分配内存:静态的分配一切。
  • 尽量不要使用浮点型;如果不得不用,那把误差放在每个地方(不要同等的测试)
  • 评论注释:
    • 不要长的散文,只要简要说明
    • 说明高层次的功能: ++i; /* increase the value of i by */ 没用!
    • 解释代码诡异的地方
    • 记录功能区和文件区
    • 这些评论应该是针对知道这个问题的聪明人而不是代码本身
    • 你不得不考虑的一些东西
    • 看代码的时候多问几次:“现在是在做什么呢?”
    • 始终说明数组的索引顺序

by MDK!2011/11/14 20:14

Complexity
Basics and order notation

The fundamental basis of complexity analysis revolves around the notion of ``big oh'' notation, for instance: O(N). This means that the algorithm's execution speed or memory usage will double when the problem size doubles. An algorithm of O(N 2) will run about four times slower (or use 4x more space) when the problem size doubles. Constant-time or space algorithms are denoted O(1). This concept applies to time and space both; here we will concentrate discussion on time.

One deduces the O() run time of a program by examining its loops. The most nested (and hence slowest) loop dominates the run time and is the only one mentioned when discussing O() notation. A program with a single loop and a nested loop (presumably loops that execute N times each) is O(N 2), even though there is also a O(N) loop present.

Of course, recursion also counts as a loop and recursive programs can have orders like O(b N), O(N!), or even O(N N).

复杂性

基础知识和通用符号

时间记号O(N 。。。。etc)。

recursion  递归!

Rules of thumb
  • When analyzing an algorithm to figure out how long it might run for a given dataset, the first rule of thumb is: modern (2004) computers can deal with 100M actions per second. In a five second time limit program, about 500M actions can be handled. Really well optimized programs might be able to double or even quadruple that number. Challenging algorithms might only be able to handle half that much. Current contests usually have a time limit of 1 second for large datasets.
  • 16MB maximum memory use
  • 210 ~approx~ 10 3
  • If you have k nested loops running about N iterations each, the program has O(N k) complexity.
  • If your program is recursive with b recursive calls per level and has l levels, the program O(b l) complexity.
  • Bear in mind that there are N! permutations and 2 n subsets or combinations of N elements when dealing with those kinds of algorithms.
  • The best times for sorting N elements are O(N log N).
  • DO THE MATH! Plug in the numbers.
经验法则:
  • 当分析这个算法对给出的测试数据会运行多长时间的时候,第一题法则是:当代计算机每秒能够处理 100M次运算。在5s限制的程序里,大概能处理500M次运算。一个优化好的程序也行真的能处理 双倍 或者 四倍这个数据。有挑战性的算法大概仅仅需要处理这个的一半。当前比赛大都对处理大数据量有1s的限制。
  • 16MB 最大存储限制
  • 210 ~approx(约)~ 10 3
  • 如果你的程序有 K 曾循环,那你的程序有O(N k)的复杂度。
  • 如果你的程序每次递归产生 b 次递归,大概有l层,那程序有O(b l)的复杂度。
  • 请记住排列 N! – 求 N 个元素的组合或者子集  2 n,处理这些算法的时间复杂度大概就是这些。
  • 处理排序 N 个元素最好的时间复杂度是O(N log N)。
  • 用数学计算!带人数字。
Examples
A single loop with N iterations is O(N):

1  sum = 0
2  for i = 1 to n
3    sum = sum + i
A double nested loop is often O(N 2):

# fill array a with N elements
1 for i = 1 to n-1
2   for j = i + 1 to n
3     if (a[i] > a[j])
         swap (a[i], a[j])
Note that even though this loop executes N x (N+1) / 2 iterations of the if statement, it is O(N 2) since doubling N quadruples the execution times.
Consider this well balanced binary tree with four levels:

An algorithm that traverses a general binary tree will have complexity O(2 N).
//这些是对每种时间复杂度的举例
Solution Paradigms

解决方案的规范

Generating vs. Filtering

Programs that generate lots of possible answers and then choose the ones that are correct (imagine an 8-queen solver) are filters. Those that hone in exactly on the correct answer without any false starts are generators. Generally, filters are easier (faster) to code and run slower. Do the math to see if a filter is good enough or if you need to try and create a generator.

生成 VS 过滤    ---    生成 VS 放弃

filters 是 程序生成很多可能的答案然后选择其中对的答案(想想 8 皇后问题)。generating 是 在严格正确的没有错误的开始上操作。大体上说,filters 是容易(快速)去码但是运行很慢。用数学计算值不值得用generating去代替filters

Precomputation

Sometimes it is helpful to generate tables or other data structures that enable the fastest possible lookup of a result. This is called precomputation (in which one trades space for time). One might either compile precomputed data into a program, calculate it when the program starts, or just remember results as you compute them. A program that must translate letters from upper to lower case when they are in upper case can do a very fast table lookup that requires no conditionals, for example. Contest problems often use prime numbers - many times it is practical to generate a long list of primes for use elsewhere in a program.

预处理(译者:我觉的是打表)

有时候生成一个表或者其他某种数据结构来查找某个结果是非常有帮助的。这叫做预处理(花空间来节省时间)。可以把要预处理的数据写成程序,在程序的一开始调用她,或者直接记住你算的结果。(译者:举了两个例子。一个是大小写转换的hash表,另一个是素数打表。不在翻译)

Decomposition (The Hardest Thing At Programming Contests)

While there are fewer than 20 basic algorithms used in contest problems, the challenge of combination problems that require a combination of two algorithms for solution is daunting. Try to separate the cues from different parts of the problem so that you can combine one algorithm with a loop or with another algorithm to solve different parts of the problem independently. Note that sometimes you can use the same algorithm twice on different (independent!) parts of your data to significantly improve your running time.

分解

虽然在竞赛中用的有少于20种基础算法,相对综合的问题需要综合两个算法,这是个艰难的挑战。尝试从问题的不同部分分别分析,这样你能够结合某个算法得的一步与其他相对独立的部分的算法解决问题。注意有时候你能用一个算法两次,在不同的部分里(独立的!)来显著的提高你的程序运行时间。

Symmetries

Many problems have symmetries (e.g., distance between a pair of points is often the same either way you traverse the points). Symmetries can be 2-way, 4-way, 8-way, and more. Try to exploit symmetries to reduce execution time.

For instance, with 4-way symmetry, you solve only one fourth of the problem and then write down the four solutions that share symmetry with the single answer (look out for self-symmetric solutions which should only be output once or twice, of course).

对称性

很多问题存在对称性(比如:一对点的距离通常是一样的,不管你怎样遍历)。对称性可能是2-way, 4-way, 8-way, 甚至更多。尝试发现对称性并且缩短运行时间。

例如,4-way 对称的,你仅仅需要算出问题的4分之1,然后用对称性和这个答案写出问题的 4 个答案(如果找到了自我对称的解决方案,那么可能只需要计算 1 或者 2次)。

Forward vs. Backward

Surprisingly, many contest problems work far better when solved backwards than when using a frontal attack. Be on the lookout for processing data in reverse order or building an attack that looks at the data in some order or fashion other than the obvious.

向前 VS 向后

令人惊讶的是,许多比赛的问题倒着想比正着想容易的多。试探性的以相反的顺序去解决问题或者从比较明显或者有规律的数据入手。

Simplification

Some problems can be rephrased into a somewhat different problem such that if you solve the new problem, you either already have or can easily find the solution to the original one; of course, you should solve the easier of the two only. Alternatively, like induction, for some problems one can make a small change to the solution of a slightly smaller problem to find the full answer.

 

简单化

一些问题可以被改写成一个率为不同的问题,就好像你在解决新问题一样,你就会从原始问题很快的找到解决方法;当然,你应该很轻松的解决。。。。。另外,像归纳,对某些问题你可以做些微小的改变找到答案,之道找到整个答案。

 

终于翻译完了,好多用词不恰当的地方,等有时候在细化一下,Stepwise refinement,then ,make pefect!

by MDK!2011/11/14 21:34

转载请标明出处。