It’s been a long time since my last blog post. Here is my recent one on Medium:
ProvenLogs is a simple yet performant logging service built with Go and ProvenDB that can continuously prove your system logs on Blockchain
Continue reading…
]]>
dispatch_barrier_asyncand
dispatch_syncoperations on a parallel queue to implement a much more efficient multi-thread safe NSMutableDictionary.
The idea is to let multiple readers in different threads use
dispatch_syncon a parallel queue, so they can access a NSMutableDictionary simultaneously. Then, when they would like to change the NSMutableDictionary, let them act as if there is only one writer writing to the dictionary. This is achieved by using
dispatch_barrier_async, which will wait for all previously scheduled readers’
dispatch_syncand the writer’s
dispatch_barrier_asyncoperations to finish before executing itself. Also, operations scheduled after a
dispatch_barrier_asyncoperation will wait for the barrier operation to finish before executing. For more details, refer to the One Resource, Multiple Readers, and a Single Writer section of this article.
The following is my implementation. I name it GGMutableDictionary.
#import <Foundation/Foundation.h> @interface GGMutableDictionary : NSMutableDictionary @end
#import "GGMutableDictionary.h" @implementation GGMutableDictionary { dispatch_queue_t isolationQueue_; NSMutableDictionary *storage_; } /** Private common init steps */ - (instancetype)initCommon { self = [super init]; if (self) { isolationQueue_ = dispatch_queue_create([@"GGMutableDictionary Isolation Queue" UTF8String], DISPATCH_QUEUE_CONCURRENT); } return self; } - (instancetype)init { self = [self initCommon]; if (self) { storage_ = [NSMutableDictionary dictionary]; } return self; } - (instancetype)initWithCapacity:(NSUInteger)numItems { self = [self initCommon]; if (self) { storage_ = [NSMutableDictionary dictionaryWithCapacity:numItems]; } return self; } - (NSDictionary *)initWithContentsOfFile:(NSString *)path { self = [self initCommon]; if (self) { storage_ = [NSMutableDictionary dictionaryWithContentsOfFile:path]; } return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [self initCommon]; if (self) { storage_ = [[NSMutableDictionary alloc] initWithCoder:aDecoder]; } return self; } - (instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt { self = [self initCommon]; if (self) { if (!objects || !keys) { [NSException raise:NSInvalidArgumentException format:@"objects and keys cannot be nil"]; } else { for (NSUInteger i = 0; i < cnt; ++i) { storage_[keys[i]] = objects[i]; } } } return self; } - (NSUInteger)count { __block NSUInteger count; dispatch_sync(isolationQueue_, ^{ count = storage_.count; }); return count; } - (id)objectForKey:(id)aKey { __block id obj; dispatch_sync(isolationQueue_, ^{ obj = storage_[aKey]; }); return obj; } - (NSEnumerator *)keyEnumerator { __block NSEnumerator *enu; dispatch_sync(isolationQueue_, ^{ enu = [storage_ keyEnumerator]; }); return enu; } - (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey { aKey = [aKey copyWithZone:NULL]; dispatch_barrier_async(isolationQueue_, ^{ storage_[aKey] = anObject; }); } - (void)removeObjectForKey:(id)aKey { dispatch_barrier_async(isolationQueue_, ^{ [storage_ removeObjectForKey:aKey]; }); } @end
]]>
'http://www.guiguan.net'in a database and replace all of its occurrences (from all columns/fields of all tables in the database) with another string
'https://www.guiguan.net', because that you just moved to a more secure world, then you can achieve this purely using native SQL statements in MySQL.
First, launch your favourite MySQL client, such as PHPMyAdmin, Adminer or just CLI, select your target database and execute the following SQL statements to define your
global_replaceprocedure:
# must change statement delimiter if we are using interactive shell DELIMITER ;; CREATE PROCEDURE `global_replace` (IN `from_string` varchar(255), IN `to_string` varchar(255)) BEGIN # declare variable DECLARE db_name, statement_string VARCHAR(255); DECLARE done INT DEFAULT FALSE; # declare cursor to receive subquery results. The subquery is # generating the UPDATE statements that will be executed next # for the actual string replacement operation to take place. # We make use of the meta database `information_schema` to # find all our needed table columns DECLARE cur CURSOR FOR (SELECT CONCAT('UPDATE `', table_name, '` SET `', column_name, '` = replace(`', column_name,'`, \'', from_string, '\', \'', to_string, '\');') FROM information_schema.columns WHERE table_schema = DATABASE()); # declare handle DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; # open cursor OPEN cur; # starts the loop the_loop: LOOP # get the values of each column into our variables FETCH cur INTO statement_string; IF done THEN LEAVE the_loop; END IF; # carry out main loop logic # we must store this local variable to a user defined # variable, because the PREPARE only accepts user # defined variable SET @tmp = statement_string; PREPARE statement FROM @tmp; EXECUTE statement; DEALLOCATE PREPARE statement; END LOOP the_loop; CLOSE cur; END;; DELIMITER ;
Then, you just call the stored procedure:
CALL global_replace('http://www.guiguan.net', 'https://www.guiguan.net');
to replace all occurrences of
'http://www.guiguan.net'across your database with the string
'https://www.guiguan.net'. Done.
In the future, you can just call
global_replaceagain to replace more strings. If you would like to remove the stored procedure, then just:
DROP PROCEDURE `global_replace`;
If you wish, you can put all the statements together in a single execution.
]]>Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
Original LeetCode problem page
An array of palindrome lengths centred at previous locations is used to make my solution run in linear time O(n)
func longestPalindrome<T: Equatable>(s: [T]) -> [T] { var sLen = s.count var curPalLen = 0 var palLens: [Int] = [] var maxPal = (idx: -1, len: -1) var i = 0 var updateMaxPal = { () -> () in if curPalLen > maxPal.len { maxPal = (palLens.count, curPalLen) } } outer: while i < sLen { // find the longest palindrome centered at current center position, i.e. // palLens.count if i > curPalLen && s[i] == s[i - curPalLen - 1] { ++i curPalLen += 2 continue } updateMaxPal() palLens.append(curPalLen) // move to next center // // we can do this either naively by just moving to next center position // of 2*n + 1 positions one by one: // i = (palLens.count - 1) / 2 + 1 // palLen = palLens.count % 2 // // or we could take advantage of the palindrome symmetry: // a b a b a b a b a // 0103050709070503010 // | // |? ? ? ? ? ? ? ? ? // 0?????????????????? // | // a|? ? ? ? ? ? ? ? // 01????????????????? // | // a|? ? ? ? ? ? ? ? // 010???????????????? // | // a b a|? ? ? ? ? ? // 0103??????????????? // | // a b a b a|? ? ? ? // 010305????????????? // | // a b a b a b a|? ? // 01030507??????????? // | // a b a b a b a b a| // 0103050709????????? let curPalLeftHalfStartIdx = palLens.count - 1 - curPalLen let curPalLeftHalfEndIdx = palLens.count - 2 for var j = curPalLeftHalfEndIdx; j >= curPalLeftHalfStartIdx; --j { let distToLeftEdge = j - curPalLeftHalfStartIdx if palLens[j] == distToLeftEdge { curPalLen = distToLeftEdge continue outer } palLens.append(min(distToLeftEdge, palLens[j])) } curPalLen = 1 ++i } updateMaxPal() let startIdx = maxPal.idx / 2 - maxPal.len / 2 return Array(s[startIdx..<startIdx + maxPal.len]) }
Check this out if you need to know more about this algorithm: http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Longest Palindromic Substring | 2.413ms |
Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height =
[2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area =
10unit.
For example,
Given height =
[2,1,5,6,2,3],
10.
Original LeetCode problem page
I used a stack to make the solution run in linear time O(n)
func largestRectangleArea(heights: [Int]) -> Int { var widths = [Int](count: heights.count, repeatedValue: 1) var stack: [Int] = [] var largestRectangleArea = 0 // calc left half of width for each reference histogram bar for var i = 0; i < heights.count; ++i { while let sIdx = stack.last { if heights[sIdx] < heights[i] { break } stack.removeLast() } if let sIdx = stack.last { widths[i] += i - sIdx - 1 } else { widths[i] += i } stack.append(i) } stack.removeAll(keepCapacity: true) // calc right half of width for each reference histogram bar while find out // the largest rectangle for var i = heights.count - 1; i >= 0; --i { while let sIdx = stack.last { if heights[sIdx] < heights[i] { break } stack.removeLast() } if let sIdx = stack.last { widths[i] += sIdx - i - 1 } else { widths[i] += heights.count - 1 - i } stack.append(i) largestRectangleArea = max(widths[i] * heights[i], largestRectangleArea) } return largestRectangleArea }
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Largest Rectangle in Histogram | 6.869ms |
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[ ["ABCE"], ["SFCS"], ["ADEE"] ]
word =
"ABCCED", -> returns
true,
"SEE", -> returns
true,
"ABCB", -> returns
false.
Original LeetCode problem page
Recursive version:
func recursiveExist(var board: [[UInt8]], var word: [UInt8]) -> Bool { if word.count == 0 { return true } var prevPoses: [Int:Void] = [:] // Use Dictionary as a hashset for var i = 0; i < board.count; ++i { for var j = 0; j < board[0].count; ++j { if _recursiveExist((j, i), &board, &word, 0, &prevPoses) { return true } } } return false }
Iterative version:
func iterativeExist(board: [[UInt8]], word: [UInt8]) -> Bool { if word.count == 0 { return true } var prevPoses: [Int:Void] = [:] // Use Dictionary as a hashset struct Frame { var pos: (x: Int, y: Int) var dir: Int } var stack: [Frame] = [] var currWordPos = 0 var pos: (x: Int, y: Int) = (-1, -1) var dir = 0 func popLastFrame() { let currFrame = stack.removeLast() pos = currFrame.pos prevPoses[combine(pos)] = nil dir = currFrame.dir + 1 --currWordPos } func searchPos(newPos: (Int, Int), oldDir: Int) { prevPoses[combine(pos)] = () stack.append(Frame(pos: pos, dir: oldDir)) pos = newPos dir = 0 ++currWordPos } for var i = 0; i < board.count; ++i { for var j = 0; j < board[0].count; ++j { pos = (j, i) search: while true { if prevPoses[combine(pos)] != nil || board[pos.y][pos.x] != word[currWordPos] { if stack.count == 0 { break search } else { popLastFrame() } } if currWordPos == word.count - 1 { return true } switch dir { case 0: if pos.y > 0 { searchPos((pos.x, pos.y - 1), 0) continue search } fallthrough case 1: if pos.x < board[0].count - 1 { searchPos((pos.x + 1, pos.y), 1) continue search } fallthrough case 2: if pos.y < board.count - 1 { searchPos((pos.x, pos.y + 1), 2) continue search } fallthrough case 3: if pos.x > 0 { searchPos((pos.x - 1, pos.y), 3) continue search } fallthrough default: if stack.count == 0 { break search } else { popLastFrame() } } } dir = 0 } } return false }
The recursive version runs twice as fast as the iterative version (57 ms vs 116 ms). In the iterative implementation, I used a stack array to record traversed node information so that the program can later backtrack if needed. This doesn’t work as efficient as the same kind of automatic information book keeping in recursion.
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Word Search | 57.457ms |
Given an input string, reverse the string word by word.
For example:
Given s = “
the sky is blue”,
blue is sky the”.
Clarification:
Original LeetCode problem page
My first version uses a bunch of Swift built-in functions
func reverseWords(s: String) -> String { var tmp = s.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) tmp = tmp.filter{ $0 != "" }.reverse() return " ".join(tmp) }
My second version does all the works myself
func reverseWords(s: String) -> String { var newS: String = "" var bufferS: String = "" func joinBufferS() { if !bufferS.isEmpty { let midS = newS.isEmpty ? "" : " " newS = bufferS + midS + newS bufferS = "" } } for c in s { if String(c) == " " { joinBufferS() } else { bufferS += String(c) } } joinBufferS() return newS }
The first version runs faster (284 ms vs 386 ms). The second version is slowed down by character comparison on line 12. There, I convert the character to be compared from Character to String type first. If I did the comparison directly using Character type, i.e.
c == " ", it is even slower (608 ms vs 386 ms). I guess the slow character comparison in Swift is due to its unicode support and the fact that each Character instance is an extended grapheme cluster instead of a simple ASCII integer. Hope Apple can improve the speed of this in the future.
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Reverse Words in a String | 268.937ms |
Given a binary tree, return the bottom-up level order traversal of its nodes’ values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree
{3,9,20,#,#,15,7},
3 / \ 9 20 / \ 15 7
return its bottom-up level order traversal as:
[ [15,7], [9,20], [3] ]
Original LeetCode problem page
This is how TreeNode class looks like:
class TreeNode { var val: Int var left: TreeNode? var right: TreeNode? init(val: Int) { self.val = val } convenience init(val: Int, left: TreeNode?, right: TreeNode?) { self.init(val: val) self.left = left self.right = right } }
I used tail recursion technique to make the recursive approach faster. The following is my recursive version of Bottom-up Breadth-first traversal (aka bottom-up level order traversal) of a binary search tree:
func recursiveLevelOrderBottom(root: TreeNode?) -> [[Int]] { if root == nil { return [] } return _recursiveLevelOrderBottom([root!], []).reverse() } func _recursiveLevelOrderBottom(queue: [TreeNode], resultSoFar: [[Int]]) -> [[Int]] { var result: [Int] = [] var queueNext: [TreeNode] = [] for node in queue { result.append(node.val) if node.left != nil { queueNext.append(node.left!) } if node.right != nil { queueNext.append(node.right!) } } if queueNext.count == 0 { return resultSoFar + [result] } else { return _recursiveLevelOrderBottom(queueNext, resultSoFar + [result]) } }
The iterative version of Bottom-up Breadth-first traversal (aka bottom-up level order traversal) of a binary search tree:
func iterativeLevelOrderBottom(root: TreeNode?) -> [[Int]] { if root == nil { return [] } var output: [[Int]] = [] var result: [Int] = [] var queue: [TreeNode] = [root!] var queueNext: [TreeNode] = [] while queue.count > 0 { for node in queue { result.append(node.val) if node.left != nil { queueNext.append(node.left!) } if node.right != nil { queueNext.append(node.right!) } } output.append(result) result.removeAll(keepCapacity: true) queue = queueNext queueNext.removeAll(keepCapacity: true) } return output.reverse() }
Even using -O compilation flag, the recursive version is still slower than the iterative counterpart with 33 ms vs 6 ms. This doesn’t follow behaviours as depicted in Depth-first traversals (NLR, LNR, LRN). The main reason is that the recursive version has to pass intermediate results deep down along with those recursive function calls. Besides, the overhead of function calls is not negligible.
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Binary Tree Level Order Traversal II | 5.797ms |
Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).
For example:
Given binary tree
{3,9,20,#,#,15,7},
3 / \ 9 20 / \ 15 7
return its level order traversal as:
[ [3], [9,20], [15,7] ]
Original LeetCode problem page
This is how TreeNode class looks like:
class TreeNode { var val: Int var left: TreeNode? var right: TreeNode? init(val: Int) { self.val = val } convenience init(val: Int, left: TreeNode?, right: TreeNode?) { self.init(val: val) self.left = left self.right = right } }
I used tail recursion technique to make the recursive approach faster. The following is my recursive version of Breadth-first traversal (aka level order traversal) of a binary search tree:
func recursiveLevelOrder(root: TreeNode?) -> [[Int]] { if root == nil { return [] } return _recursiveLevelOrder([root!], []) } func _recursiveLevelOrder(queue: [TreeNode], resultSoFar: [[Int]]) -> [[Int]] { var result: [Int] = [] var queueNext: [TreeNode] = [] for node in queue { result.append(node.val) if node.left != nil { queueNext.append(node.left!) } if node.right != nil { queueNext.append(node.right!) } } if queueNext.count == 0 { return resultSoFar + [result] } else { return _recursiveLevelOrder(queueNext, resultSoFar + [result]) } }
The iterative version of Breadth-first traversal (aka level order traversal) of a binary search tree:
func iterativeLevelOrder(root: TreeNode?) -> [[Int]] { if root == nil { return [] } var output: [[Int]] = [] var result: [Int] = [] var queue: [TreeNode] = [root!] var queueNext: [TreeNode] = [] while queue.count > 0 { for node in queue { result.append(node.val) if node.left != nil { queueNext.append(node.left!) } if node.right != nil { queueNext.append(node.right!) } } output.append(result) result.removeAll(keepCapacity: true) queue = queueNext queueNext.removeAll(keepCapacity: true) } return output }
Even using -O compilation flag, the recursive version is still slower than the iterative counterpart with 33 ms vs 6 ms. This doesn’t follow behaviours as depicted in Depth-first traversals (NLR, LNR, LRN). The main reason is that the recursive version has to pass intermediate results deep down along with those recursive function calls. Besides, the overhead of function calls is not negligible.
1: each links to a blog post of mine that is dedicated to the problem
2: total execution time of a solution on my MacBook Pro (Late 2013, 2.6 GHz Intel Core i7, 16 GB 1600 MHz DDR3). Each solution is compiled with following command:
$ swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` json.swift main.swift -o mySolution
The total execution time is the average of 10 runs.
3: these test cases are semi-automatically :P retrieved from LeetCode Online Judge system and are kept in JSON format
4: each Xcode project includes everything (my Swift solution to a problem, its JSON test cases and a driver code to test the solution on those test cases)
Problem1 | Time2 | Test Cases3 | My Xcode Project4 |
Binary Tree Level Order Traversal | 5.631ms |