complexity-cuts – skill Lập trình cho agent AI
Giảm Big-O của mã hiện có theo quy trình từng biến đổi một, với kiểm chứng–hoàn tác–dừng. Với mã mới dùng lemmaly; với cải thiện ở mức toán học thì chuyển sang mathguard.
Cài skill complexity-cuts bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/complexity-cuts-3940e.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /complexity-cuts – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/complexity-cuts-3940e.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $complexity-cuts – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/complexity-cuts-3940e.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /complexity-cuts – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/complexity-cuts-3940e.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /complexity-cuts – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
complexity-cuts — Lower Big-O on Existing Code
lemmaly prevents bad complexity before code is written. complexity-cuts fixes it after the fact: code already exists, it works, but its time or space complexity is worse than necessary.
Violating the letter of these rules is violating the spirit of the skill. Adapting "just a little" is how a faster-but-wrong rewrite ships.
When to Use This Skill
Use complexity-cuts when refactoring existing code that has poor Big-O:
- Nested loops,
O(n²)or worse scans, repeated work, redundant allocations, blown memory. - Stated symptoms: "this is slow on large inputs", "times out", "OOM", "too much memory", "reduce complexity", "optimize this algorithm".
- N+1 query patterns in ORMs (Prisma, Drizzle, SQLAlchemy, Django, ActiveRecord).
awaitinsideforover independent items causing serial latency.
For *preventing* bad complexity before code is written, use lemmaly. For math-level optimizations (Bloom, HLL, FFT, JL projection), escalate to mathguard.
The Iron Law
NO TRANSFORMATION WITHOUT EXISTING TESTS GREEN BEFORE AND AFTERIf the code has no tests, you write a characterization test first (golden input → current output). Then transform. Then verify the test still passes. If you skip this, the optimization can silently break callers — and faster-but-wrong is worse than slow-and-right.
Non-negotiable rules
1. State current and target Big-O before touching code. In one line:
- Current:
time = O(?),space = O(?) - Target:
time = O(?),space = O(?) - Dominant input dimension (n = what, how large in practice)
If you cannot state current Big-O, you do not yet understand the code. Read more.
2. Identify the bottleneck, do not guess. Point to the exact line(s) responsible for the dominant term. Nested loop? Repeated linear scan? Recomputation? Allocation inside a hot loop? The fix lives there, not elsewhere.
3. One transformation at a time, with a verify-revert-stop loop. The loop is:
1. Apply exactly one transformation from the playbook.
2. Run the existing test suite (or the characterization test you wrote per the Iron Law).
3. If any test breaks: revert immediately. Do not patch the test. Do not patch around the failure. Revert.
4. Count reverts on this piece of code. If 3 reverts in a row, STOP optimizing. The bottleneck is wrong, the transformation is wrong, or the code has invariants you have not modeled. Escalate to invariant-guard and write the missing contract — do not try a fourth transformation.
5. Only after a transformation lands green: pick the next one.
Stacked changes hide regressions. Patched tests hide regressions louder.
4. Preserve semantics exactly. Lower complexity must not change outputs, ordering guarantees, stability, or error behavior. If the optimization requires a semantic change (e.g. unordered output), call it out explicitly and confirm it is acceptable.
5. No invented numbers. Never write "10x faster" or "saves 200MB" without measuring. Write <measured: TBD> and move on, or actually measure with a representative input.
6. Always report the measured speedup ratio after a transformation lands. Once the new code is green, run a representative benchmark (same input, same machine, warm cache) and report before → after plus the ratio as N× faster (or N× less memory). One line, attached to the diff:
p50: 186 ms → 1.1 ms (169× faster, n=20,000, 200 samples)
If you cannot measure (e.g. the win is purely asymptotic on inputs you don't have), say so explicitly: asymptotic only, no measurement — O(n²) → O(n). Never silently skip this step.
The transformation playbook
The vast majority of real-world Big-O wins come from a small set of moves. Try them in this order:
Time-complexity reductions
| Smell | Fix | Typical win |
|---|---|---|
| for x in A: if x in B where B is list/array | Convert B to Set/Map once | O(n·m) → O(n+m) |
| Nested loop computing pairs/joins | Hash-join on the key; index by lookup field | O(n·m) → O(n+m) |
| Repeated .find / .indexOf / .includes inside a loop | Precompute index Map<key, item> outside loop | O(n^2) → O(n) |
| Repeated recomputation of same value | Memoize / cache by complete input key | O(n·f(n)) → O(n + f(n)) only when all n calls share one result; account for distinct keys and storage |
| Sort inside a loop | Sort once outside | O(n^2 log n) → O(n log n) |
| Linear scan for min/max/median repeatedly | Heap / sorted structure | O(n·k) → O(n log k) |
| Recursive recomputation (naive Fibonacci shape) | Memoize, or convert to iterative DP | exponential → O(n) |
| String concatenation in a loop (some langs) | Use builder / join / array.push then join | O(n^2) → O(n) |
| Repeated regex compile in loop | Compile once outside | constant-factor, large |
| Counting / grouping via nested loop | Single pass with Counter / Map<k, count> | O(n^2) → O(n) |
| Sliding-window written as nested loop | Two-pointer / windowed sum | O(n^2) → O(n) |
| Repeated prefix sums | Precompute prefix array, O(1) range queries | O(n·q) → O(n+q) |
| Pairwise distance / containment checks on intervals | Sort + sweep line | O(n^2) → O(n log n) |
| Top-K via full sort | Heap of size K | O(n log n) → O(n log k) |
| Repeated list membership in loop body | Set once, reuse | Expected O(n·m) → O(n+m), including construction |
| await inside a for over independent items | Bounded batched concurrency, honoring rate limits and cancellation | Ideally O(ceil(n/c)·latency) at concurrency c; unbounded Promise.all can exhaust resources |
| ORM query inside a loop (N+1) | IN (...) / select_related / bulk fetch | O(n) round-trips → O(1) |
Space-complexity reductions
| Smell | Fix | Typical win |
|---|---|---|
| Materializing whole list/array just to iterate | Generator / iterator / stream | O(n) → O(1) |
| Building intermediate arrays via chained .map().filter().map() on huge data | Single-pass loop or lazy pipeline | k·O(n) → O(n) (often O(1) extra) |
| Caching every intermediate result of a recursion | Rolling window (keep last k states) | O(n) → O(k) |
| Storing parents for graph traversal when only count needed | Omit parents, retain visited state when cycles or duplicate visits are possible | Removes parent storage; visited bitset is still O(n) bits |
| Copying input to mutate | In-place mutation when caller allows | O(n) → O(1) |
| Reading entire file before processing | Stream line-by-line / chunked | O(file) → O(chunk) |
| Deep-clone for safety in a loop | Clone once, or use structural sharing / immutables | O(n·m) → O(n+m) |
| Holding references that prevent GC (closures, listeners, caches) | Bound the cache (LRU), remove listeners, scope closures tightly | unbounded → bounded |
| Loading full result set from DB | Cursor / pagination / streaming query | O(rows) → O(page) |
| JSON.parse(JSON.stringify(x)) for cloning | Targeted copy or structural sharing when semantics permit; structuredClone for supported clone types | A full structuredClone still takes O(n) traversal/allocation; it is not an asymptotic reduction |
When you cannot lower asymptotic Big-O
Sometimes O(n log n) really is the floor. Then move to constant-factor wins:
- Replace pointer-chasing structures with contiguous arrays (cache locality).
- Hoist invariants out of loops.
- Avoid allocation in the hot loop (reuse buffers).
- Prefer typed arrays / native containers over boxed objects for numeric work.
- Batch syscalls / I/O.
State explicitly: "Asymptotic floor is O(n log n); applying constant-factor optimizations only."
Required workflow
For each piece of code you optimize:
1. Measure or estimate current Big-O. Write it down.
2. Identify the bottleneck line(s). Point at them.
3. Pick one transformation from the playbook. Name it.
4. Apply it. One change.
5. Verify behavior. Tests pass, or outputs match on a representative input.
6. State new Big-O. Time and space.
7. Repeat if more wins exist and are worth the complexity cost.
Canonical example — workflow vs no-workflow
The same optimization with and without the verify-revert-stop loop.
Bottleneck. getOrdersWithUsers() runs 10s on 10k orders. Cause: users.find(u => u.id === o.userId) inside the map → O(n·m).
Without the workflow — changes semantics AND patches the test
// No workflow: change semantics + the optimization in one go
export function getOrdersWithUsers(orders, users) {
const userById = Object.fromEntries(users.map(u => [u.id, u]));
return orders
.map(o => ({ ...o, user: userById[o.userId] }))
.filter(o => o.user); // silently drops orders whose user was deleted
}Faster, *and* changes the result set. Existing tests catch it — but the diff also "fixes" a flaky test by removing the assertion that checked the old behavior. Ships green. Breaks the b
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- comprehensive-review-full-review – Dùng khi làm việc với comprehensive review full review.
- comprehensive-review-pr-enhance – Tạo mô tả PR có cấu trúc từ diff, thêm danh sách kiểm tra review, đánh giá rủi ro và tóm…
- concise-planning – Dùng khi người dùng yêu cầu kế hoạch cho một tác vụ lập trình, để tạo danh sách kiểm tra…
- conductor-revert – Hoàn tác có nhận biết Git theo đơn vị công việc logic (track, phase hoặc task)
- rust-async-patterns – Làm chủ lập trình bất đồng bộ trong Rust với Tokio, async trait, xử lý lỗi và các mẫu…
- python-debugpy – Gỡ lỗi Python: REPL pdb + debugpy từ xa (DAP).
- context-driven-development – Hướng dẫn triển khai và duy trì ngữ cảnh như một artifact được quản lý cùng với mã…
- convex – Chuyên gia backend reactive Convex: thiết kế schema, hàm TypeScript, đăng ký real-time…